From a61fdf0a731e23ff7eb0cc86ba748fd9af5de879 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 21 Mar 2008 01:48:04 +0000 Subject: [PATCH] ld64-77.tar.gz --- ChangeLog | 1563 ++++++- doc/man/man1/ld.1 | 2453 +++-------- ld64.xcodeproj/project.pbxproj | 41 +- src/Architectures.hpp | 18 +- src/ExecutableFile.h | 11 +- src/LLVMReader.hpp | 561 +++ src/MachOFileAbstraction.hpp | 154 +- src/MachOReaderArchive.hpp | 99 +- src/MachOReaderDylib.hpp | 496 ++- src/MachOReaderRelocatable.hpp | 1450 +++++-- src/MachOWriterExecutable.hpp | 3776 +++++++++++++---- src/ObjectDump.cpp | 141 +- src/ObjectFile.h | 123 +- src/{SectCreate.cpp => OpaqueSection.hpp} | 95 +- src/Options.cpp | 1377 +++++- src/Options.h | 134 +- src/ld.cpp | 2041 ++++++--- src/machochecker.cpp | 203 +- unit-tests/bin/exit-non-zero-pass.pl | 9 +- unit-tests/bin/fail-if-exit-non-zero.pl | 9 +- unit-tests/bin/fail-if-exit-zero.pl | 14 +- unit-tests/bin/fail-if-no-stdin.pl | 2 + unit-tests/bin/fail-if-stdin.pl | 10 +- unit-tests/bin/fail-iff-exit-zero.pl | 16 +- unit-tests/bin/make-recursive-newtest.pl | 127 + unit-tests/bin/make-recursive.pl | 3 + unit-tests/bin/mkld | 73 + unit-tests/bin/pass-iff-exit-non-zero.pl | 29 + unit-tests/bin/pass-iff-exit-zero.pl | 10 +- unit-tests/bin/pass-iff-no-stdin.pl | 8 +- unit-tests/bin/pass-iff-stdin.pl | 7 +- unit-tests/bin/result-filter.pl | 6 +- unit-tests/bin/rm-stale-test-logs | 36 + unit-tests/clean-tests | 63 + unit-tests/include/common.makefile | 43 +- unit-tests/proctor-run | 204 + unit-tests/run-all-unit-tests | 10 +- unit-tests/run-all-unit-tests-debug | 26 + 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 + unit-tests/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 + unit-tests/test-cases/absolute-symbol/abs.s | 3 + unit-tests/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 + unit-tests/test-cases/alias-objects/Makefile | 44 + unit-tests/test-cases/alias-objects/aliases.s | 43 + unit-tests/test-cases/align-modulus/Makefile | 7 +- .../test-cases/align-modulus/comment.txt | 2 + .../test-cases/allow-stack-execute/Makefile | 2 +- .../allow-stack-execute/comment.txt | 1 + .../test-cases/allowable-client/Makefile | 34 +- .../test-cases/allowable-client/comment.txt | 1 + unit-tests/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 + unit-tests/test-cases/archive-basic/Makefile | 6 +- .../test-cases/archive-basic/comment.txt | 1 + .../test-cases/archive-duplicate/Makefile | 45 + unit-tests/test-cases/archive-duplicate/bar.c | 1 + unit-tests/test-cases/archive-duplicate/foo.c | 1 + .../test-cases/archive-duplicate/main.c | 32 + unit-tests/test-cases/archive-weak/Makefile | 7 +- .../test-cases/archive-weak/comment.txt | 7 + unit-tests/test-cases/auto-arch/Makefile | 11 +- unit-tests/test-cases/auto-arch2/Makefile | 45 + unit-tests/test-cases/auto-arch2/comment.txt | 3 + unit-tests/test-cases/auto-arch2/hello.c | 29 + unit-tests/test-cases/blank-stubs/Makefile | 23 +- unit-tests/test-cases/blank-stubs/comment.txt | 1 + unit-tests/test-cases/bundle_loader/Makefile | 6 +- .../test-cases/bundle_loader/comment.txt | 3 + .../test-cases/commons-alignment/Makefile | 37 + unit-tests/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 + unit-tests/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/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 + unit-tests/test-cases/commons-order/main.c | 4 + unit-tests/test-cases/cpu-sub-types/Makefile | 91 + .../test-cases/cpu-sub-types/comment.txt | 2 + unit-tests/test-cases/cpu-sub-types/foo.c | 3 + unit-tests/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 | 14 +- .../test-cases/dead_strip-archive/comment.txt | 1 + unit-tests/test-cases/dead_strip/Makefile | 5 +- unit-tests/test-cases/dead_strip/comment.txt | 5 + .../test-cases/dead_strip_dylibs/Makefile | 52 + unit-tests/test-cases/dead_strip_dylibs/bar.c | 5 + unit-tests/test-cases/dead_strip_dylibs/baz.c | 5 + unit-tests/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 + .../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 | 28 + .../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 | 24 +- .../dwarf-debug-notes-r/comment.txt | 5 + .../test-cases/dwarf-debug-notes/Makefile | 18 +- .../test-cases/dwarf-debug-notes/comment.txt | 4 + unit-tests/test-cases/dwarf-ignore/Makefile | 4 +- .../test-cases/dwarf-ignore/comment.txt | 1 + unit-tests/test-cases/dwarf-strip/Makefile | 7 +- unit-tests/test-cases/dwarf-strip/comment.txt | 1 + unit-tests/test-cases/dylib-aliases/Makefile | 47 + unit-tests/test-cases/dylib-aliases/bar.c | 1 + unit-tests/test-cases/dylib-aliases/foo.c | 1 + unit-tests/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 + unit-tests/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 | 41 + unit-tests/test-cases/dylib_init/comment.txt | 2 + unit-tests/test-cases/dylib_init/foo.c | 2 + unit-tests/test-cases/eh-strip-test/Makefile | 34 + .../test-cases/eh-strip-test/comment.txt | 21 + unit-tests/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 + 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 + .../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-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 | 11 +- unit-tests/test-cases/filelist/comment.txt | 1 + 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 | 48 + unit-tests/test-cases/got-elimination/bar.c | 28 + unit-tests/test-cases/got-elimination/foo.c | 42 + unit-tests/test-cases/header-pad/Makefile | 4 +- unit-tests/test-cases/header-pad/comment.txt | 1 + unit-tests/test-cases/hello-world/Makefile | 4 +- unit-tests/test-cases/hello-world/comment.txt | 1 + .../implicit-common2/Makefile.newtest | 47 + unit-tests/test-cases/implicit-common2/a.c | 7 + .../test-cases/implicit-common2/comment.txt | 1 + unit-tests/test-cases/implicit-common2/test.c | 26 + .../test-cases/implicit-common3/Makefile | 44 + unit-tests/test-cases/implicit-common3/a.c | 8 + .../test-cases/implicit-common3/comment.txt | 1 + unit-tests/test-cases/implicit-common3/test.c | 37 + .../implicit-common4/Makefile.newtest | 45 + unit-tests/test-cases/implicit-common4/a.c | 7 + .../test-cases/implicit-common4/comment.txt | 1 + unit-tests/test-cases/implicit-common4/test.c | 26 + .../implicit-common5/Makefile.newtest | 41 + unit-tests/test-cases/implicit-common5/a.c | 7 + .../test-cases/implicit-common5/comment.txt | 1 + unit-tests/test-cases/implicit-common5/test.c | 25 + unit-tests/test-cases/indirect-dylib/Makefile | 6 +- .../test-cases/indirect-dylib/comment.txt | 4 + .../test-cases/indirect-path-search/Makefile | 91 + .../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 + 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 | 4 +- .../test-cases/late-link-error/comment.txt | 2 + .../literals-coalesce-alignment/Makefile | 15 +- .../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 | 9 +- .../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 | 229 + unit-tests/test-cases/llvm-integration/a.c | 5 + unit-tests/test-cases/llvm-integration/a1.c | 10 + unit-tests/test-cases/llvm-integration/a10.c | 5 + unit-tests/test-cases/llvm-integration/a11.c | 6 + unit-tests/test-cases/llvm-integration/a12.c | 8 + unit-tests/test-cases/llvm-integration/a12.h | 8 + unit-tests/test-cases/llvm-integration/a13.cc | 3 + unit-tests/test-cases/llvm-integration/a13.h | 7 + unit-tests/test-cases/llvm-integration/a2.c | 6 + unit-tests/test-cases/llvm-integration/a3.c | 6 + unit-tests/test-cases/llvm-integration/a4.c | 6 + unit-tests/test-cases/llvm-integration/a5.c | 10 + unit-tests/test-cases/llvm-integration/a6.c | 10 + unit-tests/test-cases/llvm-integration/a7.c | 11 + unit-tests/test-cases/llvm-integration/a8.c | 23 + unit-tests/test-cases/llvm-integration/a9.c | 25 + .../test-cases/llvm-integration/a9.list | 3 + unit-tests/test-cases/llvm-integration/b.c | 3 + unit-tests/test-cases/llvm-integration/b1.c | 4 + unit-tests/test-cases/llvm-integration/b10.c | 7 + unit-tests/test-cases/llvm-integration/b10.h | 6 + unit-tests/test-cases/llvm-integration/b2.c | 9 + unit-tests/test-cases/llvm-integration/b3.c | 4 + unit-tests/test-cases/llvm-integration/b4.c | 13 + unit-tests/test-cases/llvm-integration/b5.c | 4 + unit-tests/test-cases/llvm-integration/b7.c | 7 + unit-tests/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/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 + unit-tests/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 + unit-tests/test-cases/main-stripped/Makefile | 38 + unit-tests/test-cases/main-stripped/main.c | 34 + unit-tests/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 | 11 +- .../multiple-entry-points/comment.txt | 3 + .../no-dynamic-common/Makefile.newtest | 39 + unit-tests/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 | 13 +- unit-tests/test-cases/no-uuid/comment.txt | 1 + unit-tests/test-cases/non-lazy-r/Makefile | 59 + unit-tests/test-cases/non-lazy-r/foo.c | 12 + unit-tests/test-cases/non-lazy-r/other.c | 2 + unit-tests/test-cases/objc-gc-checks/Makefile | 75 + unit-tests/test-cases/objc-gc-checks/bar.m | 12 + .../test-cases/objc-gc-checks/comment.txt | 1 + unit-tests/test-cases/objc-gc-checks/foo.m | 12 + .../test-cases/objc-references/Makefile | 19 +- .../test-cases/objc-references/comment.txt | 1 + unit-tests/test-cases/order_file-ans/Makefile | 40 + unit-tests/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 + unit-tests/test-cases/order_file/main1.order | 4 + .../test-cases/order_file/main2.expected | 11 + unit-tests/test-cases/order_file/main2.order | 6 + .../test-cases/order_file/main3.expected | 4 + unit-tests/test-cases/order_file/main3.order | 8 + .../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 | 13 +- .../test-cases/private-non-lazy/comment.txt | 1 + .../test-cases/re-export-cases/Makefile | 167 + unit-tests/test-cases/re-export-cases/bar.c | 5 + unit-tests/test-cases/re-export-cases/baz.c | 5 + unit-tests/test-cases/re-export-cases/foo.c | 4 + unit-tests/test-cases/re-export-flag/Makefile | 48 + unit-tests/test-cases/re-export-flag/bar.c | 5 + unit-tests/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 | 18 +- .../test-cases/read-only-relocs/comment.txt | 1 + unit-tests/test-cases/rebase-basic/Makefile | 21 +- .../test-cases/rebase-basic/comment.txt | 1 + unit-tests/test-cases/relocs-asm/Makefile | 11 +- unit-tests/test-cases/relocs-asm/comment.txt | 3 + unit-tests/test-cases/relocs-asm/relocs-asm.s | 39 +- unit-tests/test-cases/relocs-c/Makefile | 12 +- unit-tests/test-cases/relocs-c2/Makefile | 59 + unit-tests/test-cases/relocs-c2/comment.txt | 5 + unit-tests/test-cases/relocs-c2/test.c | 76 + .../test-cases/relocs-literals/Makefile | 18 +- unit-tests/test-cases/relocs-literals/test.c | 7 + .../test-cases/relocs-literals2/Makefile | 50 + unit-tests/test-cases/relocs-literals2/test.c | 54 + .../test-cases/relocs-literals3/Makefile | 48 + .../test-cases/relocs-literals3/comment.txt | 3 + unit-tests/test-cases/relocs-literals3/test.c | 47 + unit-tests/test-cases/relocs-objc/Makefile | 10 +- unit-tests/test-cases/relocs-objc/comment.txt | 3 + unit-tests/test-cases/segment-order/Makefile | 37 + .../test-cases/segment-order/expected.order | 3 + unit-tests/test-cases/segment-order/main.c | 4 + unit-tests/test-cases/segment-order/segJJJ.s | 7 + unit-tests/test-cases/segment-order/segKKK.s | 7 + unit-tests/test-cases/segment-order/segLLL.s | 7 + unit-tests/test-cases/special-labels/Makefile | 41 + unit-tests/test-cases/special-labels/extra.s | 9 + unit-tests/test-cases/special-labels/main.c | 29 + unit-tests/test-cases/stabs-coalesce/Makefile | 12 +- .../test-cases/stabs-coalesce/comment.txt | 3 + .../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 | 71 + .../test-cases/stack_addr_size/comment.txt | 11 + unit-tests/test-cases/stack_addr_size/main.c | 29 + .../test-cases/stack_size_no_addr/Makefile | 78 + .../test-cases/stack_size_no_addr/comment.txt | 11 + .../test-cases/stack_size_no_addr/main.c | 29 + .../test-cases/static-executable/Makefile | 19 +- .../test-cases/static-executable/comment.txt | 1 + .../test-cases/static-strip/Makefile.newtest | 40 + .../test-cases/static-strip/comment.txt | 1 + unit-tests/test-cases/static-strip/test.c | 28 + unit-tests/test-cases/strip-test2/Makefile | 70 + unit-tests/test-cases/strip-test2/comment.txt | 21 + unit-tests/test-cases/strip-test2/main.cxx | 6 + .../test-cases/strip-test3/Makefile.newtest | 71 + unit-tests/test-cases/strip-test3/comment.txt | 21 + unit-tests/test-cases/strip-test3/main.cxx | 6 + unit-tests/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/Makefile | 41 + unit-tests/test-cases/stub-generation/test.c | 40 + unit-tests/test-cases/symbol-moving/Makefile | 93 + unit-tests/test-cases/symbol-moving/aaa.c | 3 + unit-tests/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 + unit-tests/test-cases/symbol-moving/bnota.c | 25 + unit-tests/test-cases/symbol-moving/foo.c | 3 + unit-tests/test-cases/symbol-moving/main.c | 17 + .../test-cases/tentative-to-real/Makefile | 17 +- .../test-cases/tentative-to-real/comment.txt | 1 + .../undefined-dynamic-lookup/Makefile | 47 + .../undefined-dynamic-lookup/main.c | 32 + .../test-cases/weak-def-ordinal/Makefile | 50 + unit-tests/test-cases/weak-def-ordinal/bar.c | 6 + unit-tests/test-cases/weak-def-ordinal/foo.c | 11 + unit-tests/test-cases/weak-def-ordinal/main.c | 35 + unit-tests/test-cases/weak_import/Makefile | 8 +- .../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 + unit-tests/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/zero-fill/Makefile | 4 +- unit-tests/test-cases/zero-fill2/Makefile | 41 + unit-tests/test-cases/zero-fill2/comment.txt | 1 + unit-tests/test-cases/zero-fill2/test.c | 51 + unit-tests/test-cases/zero-fill3/Makefile | 47 + unit-tests/test-cases/zero-fill3/comment.txt | 1 + unit-tests/test-cases/zero-fill3/test.c | 63 + 458 files changed, 19756 insertions(+), 4477 deletions(-) create mode 100644 src/LLVMReader.hpp rename src/{SectCreate.cpp => OpaqueSection.hpp} (51%) create mode 100755 unit-tests/bin/make-recursive-newtest.pl create mode 100755 unit-tests/bin/mkld create mode 100755 unit-tests/bin/pass-iff-exit-non-zero.pl create mode 100755 unit-tests/bin/rm-stale-test-logs create mode 100755 unit-tests/clean-tests create mode 100755 unit-tests/proctor-run create mode 100755 unit-tests/run-all-unit-tests-debug create mode 100644 unit-tests/src/Makefile create mode 100644 unit-tests/src/results-to-xml.cpp create mode 100644 unit-tests/src/xmlparser/xmlparser.1 create mode 100644 unit-tests/src/xmlparser/xmlparser.m create mode 100644 unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj create mode 100644 unit-tests/src/xmlparser/xmlparser_Prefix.pch create mode 100644 unit-tests/test-cases/16-byte-alignment/Makefile create mode 100644 unit-tests/test-cases/16-byte-alignment/comment.txt create mode 100644 unit-tests/test-cases/16-byte-alignment/tl_test2.c create mode 100644 unit-tests/test-cases/absolute-symbol/Makefile create mode 100644 unit-tests/test-cases/absolute-symbol/abs.s create mode 100644 unit-tests/test-cases/absolute-symbol/main.c create mode 100644 unit-tests/test-cases/alias-command-line/Makefile create mode 100644 unit-tests/test-cases/alias-command-line/aliases.s create mode 100644 unit-tests/test-cases/alias-command-line/aliases.txt create mode 100644 unit-tests/test-cases/alias-objects/Makefile create mode 100644 unit-tests/test-cases/alias-objects/aliases.s create mode 100644 unit-tests/test-cases/align-modulus/comment.txt create mode 100644 unit-tests/test-cases/allow-stack-execute/comment.txt create mode 100644 unit-tests/test-cases/allowable-client/comment.txt create mode 100644 unit-tests/test-cases/archive-ObjC/Makefile create mode 100644 unit-tests/test-cases/archive-ObjC/bar.c create mode 100644 unit-tests/test-cases/archive-ObjC/baz.m create mode 100644 unit-tests/test-cases/archive-ObjC/foo.m create mode 100644 unit-tests/test-cases/archive-ObjC/main.c create mode 100644 unit-tests/test-cases/archive-basic/comment.txt create mode 100644 unit-tests/test-cases/archive-duplicate/Makefile create mode 100644 unit-tests/test-cases/archive-duplicate/bar.c create mode 100644 unit-tests/test-cases/archive-duplicate/foo.c create mode 100644 unit-tests/test-cases/archive-duplicate/main.c create mode 100644 unit-tests/test-cases/archive-weak/comment.txt create mode 100644 unit-tests/test-cases/auto-arch2/Makefile create mode 100644 unit-tests/test-cases/auto-arch2/comment.txt create mode 100644 unit-tests/test-cases/auto-arch2/hello.c create mode 100644 unit-tests/test-cases/blank-stubs/comment.txt create mode 100644 unit-tests/test-cases/bundle_loader/comment.txt create mode 100644 unit-tests/test-cases/commons-alignment/Makefile create mode 100644 unit-tests/test-cases/commons-alignment/foo.s create mode 100644 unit-tests/test-cases/commons-coalesced-dead_strip/Makefile create mode 100644 unit-tests/test-cases/commons-coalesced-dead_strip/a.c create mode 100644 unit-tests/test-cases/commons-coalesced-dead_strip/b.c create mode 100644 unit-tests/test-cases/commons-coalesced-dead_strip/c.c create mode 100644 unit-tests/test-cases/commons-coalesced-dead_strip/c.h create mode 100644 unit-tests/test-cases/commons-mixed/Makefile create mode 100644 unit-tests/test-cases/commons-mixed/bar.c create mode 100644 unit-tests/test-cases/commons-mixed/foo.c create mode 100644 unit-tests/test-cases/commons-order/Makefile create mode 100644 unit-tests/test-cases/commons-order/bar.c create mode 100644 unit-tests/test-cases/commons-order/baz.c create mode 100644 unit-tests/test-cases/commons-order/expected.order create mode 100644 unit-tests/test-cases/commons-order/foo.c create mode 100644 unit-tests/test-cases/commons-order/main.c create mode 100644 unit-tests/test-cases/cpu-sub-types/Makefile create mode 100644 unit-tests/test-cases/cpu-sub-types/comment.txt create mode 100644 unit-tests/test-cases/cpu-sub-types/foo.c create mode 100644 unit-tests/test-cases/cpu-sub-types/main.c create mode 100644 unit-tests/test-cases/dead_strip-archive-global/Makefile create mode 100644 unit-tests/test-cases/dead_strip-archive-global/foo.c create mode 100644 unit-tests/test-cases/dead_strip-archive-global/main.c create mode 100644 unit-tests/test-cases/dead_strip-archive/comment.txt create mode 100644 unit-tests/test-cases/dead_strip/comment.txt create mode 100644 unit-tests/test-cases/dead_strip_dylibs/Makefile create mode 100644 unit-tests/test-cases/dead_strip_dylibs/bar.c create mode 100644 unit-tests/test-cases/dead_strip_dylibs/baz.c create mode 100644 unit-tests/test-cases/dead_strip_dylibs/foo.c create mode 100644 unit-tests/test-cases/dead_strip_dylibs/main.c create mode 100644 unit-tests/test-cases/dead_strip_section_attribute/Makefile create mode 100644 unit-tests/test-cases/dead_strip_section_attribute/comment.txt create mode 100644 unit-tests/test-cases/dead_strip_section_attribute/main.c create mode 100644 unit-tests/test-cases/dtrace-static-probes/Makefile create mode 100644 unit-tests/test-cases/dtrace-static-probes/bar.d create mode 100644 unit-tests/test-cases/dtrace-static-probes/comment.txt create mode 100644 unit-tests/test-cases/dtrace-static-probes/foo.d create mode 100644 unit-tests/test-cases/dtrace-static-probes/main.c create mode 100644 unit-tests/test-cases/dwarf-archive-all_load/Makefile create mode 100644 unit-tests/test-cases/dwarf-archive-all_load/bar.c create mode 100644 unit-tests/test-cases/dwarf-archive-all_load/baz.c create mode 100644 unit-tests/test-cases/dwarf-archive-all_load/comment.txt create mode 100644 unit-tests/test-cases/dwarf-archive-all_load/expected-stabs create mode 100644 unit-tests/test-cases/dwarf-archive-all_load/foo.c create mode 100755 unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl create mode 100644 unit-tests/test-cases/dwarf-debug-notes-r/comment.txt create mode 100644 unit-tests/test-cases/dwarf-debug-notes/comment.txt create mode 100644 unit-tests/test-cases/dwarf-ignore/comment.txt create mode 100644 unit-tests/test-cases/dwarf-strip/comment.txt create mode 100644 unit-tests/test-cases/dylib-aliases/Makefile create mode 100644 unit-tests/test-cases/dylib-aliases/bar.c create mode 100644 unit-tests/test-cases/dylib-aliases/foo.c create mode 100644 unit-tests/test-cases/dylib-aliases/main.c create mode 100644 unit-tests/test-cases/dylib-re-export-cycle/Makefile create mode 100644 unit-tests/test-cases/dylib-re-export-cycle/bar.c create mode 100644 unit-tests/test-cases/dylib-re-export-cycle/foo.c create mode 100644 unit-tests/test-cases/dylib-re-export-cycle/main.c create mode 100644 unit-tests/test-cases/dylib_file-missing/Makefile create mode 100644 unit-tests/test-cases/dylib_file-missing/bar.c create mode 100644 unit-tests/test-cases/dylib_file-missing/foo.c create mode 100644 unit-tests/test-cases/dylib_file-missing/main.c create mode 100644 unit-tests/test-cases/dylib_file/Makefile create mode 100644 unit-tests/test-cases/dylib_file/bar.c create mode 100644 unit-tests/test-cases/dylib_file/comment.txt create mode 100644 unit-tests/test-cases/dylib_file/foo.c create mode 100644 unit-tests/test-cases/dylib_file/main.c create mode 100644 unit-tests/test-cases/dylib_init/Makefile create mode 100644 unit-tests/test-cases/dylib_init/comment.txt create mode 100644 unit-tests/test-cases/dylib_init/foo.c create mode 100644 unit-tests/test-cases/eh-strip-test/Makefile create mode 100644 unit-tests/test-cases/eh-strip-test/comment.txt create mode 100644 unit-tests/test-cases/eh-strip-test/main.cxx create mode 100644 unit-tests/test-cases/eh_frame/Makefile create mode 100644 unit-tests/test-cases/eh_frame/bar.cxx create mode 100644 unit-tests/test-cases/eh_frame/foo.cxx create mode 100644 unit-tests/test-cases/end-label/Makefile create mode 100644 unit-tests/test-cases/end-label/bar.s create mode 100644 unit-tests/test-cases/end-label/foo.s create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/Makefile create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect1 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect2 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect3 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect4 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect5 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect6 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect7 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/expect8 create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/foo.c create mode 100644 unit-tests/test-cases/exported-symbols-wildcards/list5 create mode 100644 unit-tests/test-cases/exported_symbols_list-eol/Makefile create mode 100644 unit-tests/test-cases/exported_symbols_list-eol/expected.nm create mode 100644 unit-tests/test-cases/exported_symbols_list-eol/test.c create mode 100644 unit-tests/test-cases/exported_symbols_list-eol/test.exp create mode 100644 unit-tests/test-cases/exported_symbols_list-r/Makefile create mode 100644 unit-tests/test-cases/exported_symbols_list-r/test-bad.exp create mode 100644 unit-tests/test-cases/exported_symbols_list-r/test.c create mode 100644 unit-tests/test-cases/exported_symbols_list-r/test.exp create mode 100644 unit-tests/test-cases/external-reloc-sorting/Makefile create mode 100644 unit-tests/test-cases/external-reloc-sorting/foo.c create mode 100644 unit-tests/test-cases/external-reloc-sorting/main.c create mode 100644 unit-tests/test-cases/filelist/comment.txt create mode 100644 unit-tests/test-cases/flat-dylib/Makefile create mode 100644 unit-tests/test-cases/flat-dylib/main.c create mode 100644 unit-tests/test-cases/flat-indirect-undefines/Makefile create mode 100644 unit-tests/test-cases/flat-indirect-undefines/bar.c create mode 100644 unit-tests/test-cases/flat-indirect-undefines/foo.c create mode 100644 unit-tests/test-cases/flat-indirect-undefines/main.c create mode 100644 unit-tests/test-cases/flat-main/Makefile create mode 100644 unit-tests/test-cases/flat-main/main.c create mode 100644 unit-tests/test-cases/got-elimination/Makefile create mode 100644 unit-tests/test-cases/got-elimination/bar.c create mode 100644 unit-tests/test-cases/got-elimination/foo.c create mode 100644 unit-tests/test-cases/header-pad/comment.txt create mode 100644 unit-tests/test-cases/hello-world/comment.txt create mode 100644 unit-tests/test-cases/implicit-common2/Makefile.newtest create mode 100644 unit-tests/test-cases/implicit-common2/a.c create mode 100644 unit-tests/test-cases/implicit-common2/comment.txt create mode 100644 unit-tests/test-cases/implicit-common2/test.c create mode 100644 unit-tests/test-cases/implicit-common3/Makefile create mode 100644 unit-tests/test-cases/implicit-common3/a.c create mode 100644 unit-tests/test-cases/implicit-common3/comment.txt create mode 100644 unit-tests/test-cases/implicit-common3/test.c create mode 100644 unit-tests/test-cases/implicit-common4/Makefile.newtest create mode 100644 unit-tests/test-cases/implicit-common4/a.c create mode 100644 unit-tests/test-cases/implicit-common4/comment.txt create mode 100644 unit-tests/test-cases/implicit-common4/test.c create mode 100644 unit-tests/test-cases/implicit-common5/Makefile.newtest create mode 100644 unit-tests/test-cases/implicit-common5/a.c create mode 100644 unit-tests/test-cases/implicit-common5/comment.txt create mode 100644 unit-tests/test-cases/implicit-common5/test.c create mode 100644 unit-tests/test-cases/indirect-dylib/comment.txt create mode 100644 unit-tests/test-cases/indirect-path-search/Makefile create mode 100644 unit-tests/test-cases/indirect-path-search/bar.c create mode 100644 unit-tests/test-cases/indirect-path-search/baz.c create mode 100644 unit-tests/test-cases/indirect-path-search/foo.c create mode 100644 unit-tests/test-cases/indirect-path-search/main.c create mode 100644 unit-tests/test-cases/large-data/Makefile create mode 100644 unit-tests/test-cases/large-data/test1.c create mode 100644 unit-tests/test-cases/large-data/test2.c create mode 100644 unit-tests/test-cases/large-data/test3.c create mode 100644 unit-tests/test-cases/large-data/test4.c create mode 100644 unit-tests/test-cases/late-link-error/comment.txt create mode 100644 unit-tests/test-cases/literals-coalesce-alignment2/Makefile create mode 100644 unit-tests/test-cases/literals-coalesce-alignment2/comment.txt create mode 100644 unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s create mode 100644 unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s create mode 100644 unit-tests/test-cases/literals-coalesce-alignment3/Makefile create mode 100644 unit-tests/test-cases/literals-coalesce-alignment3/comment.txt create mode 100644 unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s create mode 100644 unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s create mode 100644 unit-tests/test-cases/literals-coalesce2/Makefile.newtest create mode 100644 unit-tests/test-cases/literals-coalesce2/comment.txt create mode 100644 unit-tests/test-cases/literals-coalesce2/literals.s create mode 100755 unit-tests/test-cases/literals-coalesce2/test.sh create mode 100644 unit-tests/test-cases/llvm-integration/Makefile create mode 100644 unit-tests/test-cases/llvm-integration/a.c create mode 100644 unit-tests/test-cases/llvm-integration/a1.c create mode 100644 unit-tests/test-cases/llvm-integration/a10.c create mode 100644 unit-tests/test-cases/llvm-integration/a11.c create mode 100644 unit-tests/test-cases/llvm-integration/a12.c create mode 100644 unit-tests/test-cases/llvm-integration/a12.h create mode 100644 unit-tests/test-cases/llvm-integration/a13.cc create mode 100644 unit-tests/test-cases/llvm-integration/a13.h create mode 100644 unit-tests/test-cases/llvm-integration/a2.c create mode 100644 unit-tests/test-cases/llvm-integration/a3.c create mode 100644 unit-tests/test-cases/llvm-integration/a4.c create mode 100644 unit-tests/test-cases/llvm-integration/a5.c create mode 100644 unit-tests/test-cases/llvm-integration/a6.c create mode 100644 unit-tests/test-cases/llvm-integration/a7.c create mode 100644 unit-tests/test-cases/llvm-integration/a8.c create mode 100644 unit-tests/test-cases/llvm-integration/a9.c create mode 100644 unit-tests/test-cases/llvm-integration/a9.list create mode 100644 unit-tests/test-cases/llvm-integration/b.c create mode 100644 unit-tests/test-cases/llvm-integration/b1.c create mode 100644 unit-tests/test-cases/llvm-integration/b10.c create mode 100644 unit-tests/test-cases/llvm-integration/b10.h create mode 100644 unit-tests/test-cases/llvm-integration/b2.c create mode 100644 unit-tests/test-cases/llvm-integration/b3.c create mode 100644 unit-tests/test-cases/llvm-integration/b4.c create mode 100644 unit-tests/test-cases/llvm-integration/b5.c create mode 100644 unit-tests/test-cases/llvm-integration/b7.c create mode 100644 unit-tests/test-cases/llvm-integration/main.c create mode 100644 unit-tests/test-cases/llvm-integration/main1.c create mode 100644 unit-tests/test-cases/llvm-integration/main10.c create mode 100644 unit-tests/test-cases/llvm-integration/main11.c create mode 100644 unit-tests/test-cases/llvm-integration/main12.c create mode 100644 unit-tests/test-cases/llvm-integration/main13.cc create mode 100644 unit-tests/test-cases/llvm-integration/main2.c create mode 100644 unit-tests/test-cases/llvm-integration/main3.c create mode 100644 unit-tests/test-cases/llvm-integration/main4.c create mode 100644 unit-tests/test-cases/llvm-integration/main5.c create mode 100644 unit-tests/test-cases/llvm-integration/main6.c create mode 100644 unit-tests/test-cases/llvm-integration/main7.c create mode 100644 unit-tests/test-cases/llvm-integration/main8.c create mode 100644 unit-tests/test-cases/llvm-integration/main9.c create mode 100644 unit-tests/test-cases/loader_path/Makefile create mode 100644 unit-tests/test-cases/loader_path/bar.c create mode 100644 unit-tests/test-cases/loader_path/foo.c create mode 100644 unit-tests/test-cases/loader_path/main.c create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/Makefile create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/a.expect create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/a.list create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/b.expect create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/b.list create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/c.list create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/foo.c create mode 100644 unit-tests/test-cases/local-symbol-partial-stripping/main.c create mode 100644 unit-tests/test-cases/main-stripped/Makefile create mode 100644 unit-tests/test-cases/main-stripped/main.c create mode 100644 unit-tests/test-cases/main-stripped/main.exp create mode 100644 unit-tests/test-cases/missing-option-args/Makefile create mode 100644 unit-tests/test-cases/missing-option-args/comment.txt create mode 100644 unit-tests/test-cases/multiple-entry-points/comment.txt create mode 100644 unit-tests/test-cases/no-dynamic-common/Makefile.newtest create mode 100644 unit-tests/test-cases/no-dynamic-common/a.c create mode 100644 unit-tests/test-cases/no-dynamic-common/comment.txt create mode 100644 unit-tests/test-cases/no-dynamic-common/test.c create mode 100644 unit-tests/test-cases/no-uuid/comment.txt create mode 100644 unit-tests/test-cases/non-lazy-r/Makefile create mode 100644 unit-tests/test-cases/non-lazy-r/foo.c create mode 100644 unit-tests/test-cases/non-lazy-r/other.c create mode 100644 unit-tests/test-cases/objc-gc-checks/Makefile create mode 100644 unit-tests/test-cases/objc-gc-checks/bar.m create mode 100644 unit-tests/test-cases/objc-gc-checks/comment.txt create mode 100644 unit-tests/test-cases/objc-gc-checks/foo.m create mode 100644 unit-tests/test-cases/objc-references/comment.txt create mode 100644 unit-tests/test-cases/order_file-ans/Makefile create mode 100644 unit-tests/test-cases/order_file-ans/main.cxx create mode 100644 unit-tests/test-cases/order_file-ans/main.expected create mode 100644 unit-tests/test-cases/order_file-ans/main.order create mode 100644 unit-tests/test-cases/order_file/Makefile create mode 100644 unit-tests/test-cases/order_file/extra.s create mode 100644 unit-tests/test-cases/order_file/main.c create mode 100644 unit-tests/test-cases/order_file/main1.expected create mode 100644 unit-tests/test-cases/order_file/main1.order create mode 100644 unit-tests/test-cases/order_file/main2.expected create mode 100644 unit-tests/test-cases/order_file/main2.order create mode 100644 unit-tests/test-cases/order_file/main3.expected create mode 100644 unit-tests/test-cases/order_file/main3.order create mode 100644 unit-tests/test-cases/prebound-split-seg/Makefile create mode 100644 unit-tests/test-cases/prebound-split-seg/address_table create mode 100644 unit-tests/test-cases/prebound-split-seg/bar.c create mode 100644 unit-tests/test-cases/private-non-lazy/comment.txt create mode 100644 unit-tests/test-cases/re-export-cases/Makefile create mode 100644 unit-tests/test-cases/re-export-cases/bar.c create mode 100644 unit-tests/test-cases/re-export-cases/baz.c create mode 100644 unit-tests/test-cases/re-export-cases/foo.c create mode 100644 unit-tests/test-cases/re-export-flag/Makefile create mode 100644 unit-tests/test-cases/re-export-flag/bar.c create mode 100644 unit-tests/test-cases/re-export-flag/foo.c create mode 100644 unit-tests/test-cases/re-export-optimizations/Makefile create mode 100644 unit-tests/test-cases/re-export-optimizations/bar.c create mode 100644 unit-tests/test-cases/re-export-optimizations/foo.c create mode 100644 unit-tests/test-cases/re-export-optimizations/main.c create mode 100644 unit-tests/test-cases/re-export-relative-paths/Makefile create mode 100644 unit-tests/test-cases/re-export-relative-paths/bar.c create mode 100644 unit-tests/test-cases/re-export-relative-paths/foo.c create mode 100644 unit-tests/test-cases/re-export-relative-paths/main.c create mode 100644 unit-tests/test-cases/re-export-relative-paths/wrap.c create mode 100644 unit-tests/test-cases/read-only-relocs/comment.txt create mode 100644 unit-tests/test-cases/rebase-basic/comment.txt create mode 100644 unit-tests/test-cases/relocs-asm/comment.txt create mode 100644 unit-tests/test-cases/relocs-c2/Makefile create mode 100644 unit-tests/test-cases/relocs-c2/comment.txt create mode 100644 unit-tests/test-cases/relocs-c2/test.c create mode 100644 unit-tests/test-cases/relocs-literals2/Makefile create mode 100644 unit-tests/test-cases/relocs-literals2/test.c create mode 100644 unit-tests/test-cases/relocs-literals3/Makefile create mode 100644 unit-tests/test-cases/relocs-literals3/comment.txt create mode 100644 unit-tests/test-cases/relocs-literals3/test.c create mode 100644 unit-tests/test-cases/relocs-objc/comment.txt create mode 100644 unit-tests/test-cases/segment-order/Makefile create mode 100644 unit-tests/test-cases/segment-order/expected.order create mode 100644 unit-tests/test-cases/segment-order/main.c create mode 100644 unit-tests/test-cases/segment-order/segJJJ.s create mode 100644 unit-tests/test-cases/segment-order/segKKK.s create mode 100644 unit-tests/test-cases/segment-order/segLLL.s create mode 100644 unit-tests/test-cases/special-labels/Makefile create mode 100644 unit-tests/test-cases/special-labels/extra.s create mode 100644 unit-tests/test-cases/special-labels/main.c create mode 100644 unit-tests/test-cases/stabs-coalesce/comment.txt create mode 100644 unit-tests/test-cases/stabs-directory-slash/Makefile create mode 100644 unit-tests/test-cases/stabs-directory-slash/main.c create mode 100644 unit-tests/test-cases/stack_addr_no_size/Makefile.newtest create mode 100644 unit-tests/test-cases/stack_addr_no_size/comment.txt create mode 100644 unit-tests/test-cases/stack_addr_no_size/main.c create mode 100644 unit-tests/test-cases/stack_addr_size/Makefile create mode 100644 unit-tests/test-cases/stack_addr_size/comment.txt create mode 100644 unit-tests/test-cases/stack_addr_size/main.c create mode 100644 unit-tests/test-cases/stack_size_no_addr/Makefile create mode 100644 unit-tests/test-cases/stack_size_no_addr/comment.txt create mode 100644 unit-tests/test-cases/stack_size_no_addr/main.c create mode 100644 unit-tests/test-cases/static-executable/comment.txt create mode 100644 unit-tests/test-cases/static-strip/Makefile.newtest create mode 100644 unit-tests/test-cases/static-strip/comment.txt create mode 100644 unit-tests/test-cases/static-strip/test.c create mode 100644 unit-tests/test-cases/strip-test2/Makefile create mode 100644 unit-tests/test-cases/strip-test2/comment.txt create mode 100644 unit-tests/test-cases/strip-test2/main.cxx create mode 100644 unit-tests/test-cases/strip-test3/Makefile.newtest create mode 100644 unit-tests/test-cases/strip-test3/comment.txt create mode 100644 unit-tests/test-cases/strip-test3/main.cxx create mode 100644 unit-tests/test-cases/strip_local/Makefile create mode 100644 unit-tests/test-cases/strip_local/foo.c create mode 100644 unit-tests/test-cases/strip_local/hello.c create mode 100644 unit-tests/test-cases/stripped-indirect-symbol-table/Makefile create mode 100644 unit-tests/test-cases/stripped-indirect-symbol-table/a.c create mode 100644 unit-tests/test-cases/stripped-indirect-symbol-table/b.c create mode 100644 unit-tests/test-cases/stripped-indirect-symbol-table/c.c create mode 100644 unit-tests/test-cases/stripped-indirect-symbol-table/func.c create mode 100644 unit-tests/test-cases/stripped-indirect-symbol-table/strip.list create mode 100644 unit-tests/test-cases/stub-generation/Makefile create mode 100644 unit-tests/test-cases/stub-generation/test.c create mode 100644 unit-tests/test-cases/symbol-moving/Makefile create mode 100644 unit-tests/test-cases/symbol-moving/aaa.c create mode 100644 unit-tests/test-cases/symbol-moving/anotb.c create mode 100644 unit-tests/test-cases/symbol-moving/bar.c create mode 100644 unit-tests/test-cases/symbol-moving/bbb.c create mode 100644 unit-tests/test-cases/symbol-moving/bnota.c create mode 100644 unit-tests/test-cases/symbol-moving/foo.c create mode 100644 unit-tests/test-cases/symbol-moving/main.c create mode 100644 unit-tests/test-cases/tentative-to-real/comment.txt create mode 100644 unit-tests/test-cases/undefined-dynamic-lookup/Makefile create mode 100644 unit-tests/test-cases/undefined-dynamic-lookup/main.c create mode 100644 unit-tests/test-cases/weak-def-ordinal/Makefile create mode 100644 unit-tests/test-cases/weak-def-ordinal/bar.c create mode 100644 unit-tests/test-cases/weak-def-ordinal/foo.c create mode 100644 unit-tests/test-cases/weak-def-ordinal/main.c create mode 100644 unit-tests/test-cases/weak_import2/Makefile.newtest create mode 100644 unit-tests/test-cases/weak_import2/comment.txt create mode 100644 unit-tests/test-cases/weak_import2/foo.c create mode 100644 unit-tests/test-cases/weak_import2/foo.h create mode 100644 unit-tests/test-cases/weak_import2/foo1.c create mode 100644 unit-tests/test-cases/weak_import2/main.c create mode 100644 unit-tests/test-cases/weak_import3/Makefile create mode 100644 unit-tests/test-cases/weak_import3/comment.txt create mode 100644 unit-tests/test-cases/weak_import3/foo.c create mode 100644 unit-tests/test-cases/weak_import3/foo.h create mode 100644 unit-tests/test-cases/weak_import3/foo1.c create mode 100644 unit-tests/test-cases/weak_import3/main.c create mode 100644 unit-tests/test-cases/zero-fill2/Makefile create mode 100644 unit-tests/test-cases/zero-fill2/comment.txt create mode 100644 unit-tests/test-cases/zero-fill2/test.c create mode 100644 unit-tests/test-cases/zero-fill3/Makefile create mode 100644 unit-tests/test-cases/zero-fill3/comment.txt create mode 100644 unit-tests/test-cases/zero-fill3/test.c diff --git a/ChangeLog b/ChangeLog index ce470b0..7a63394 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,1448 @@ +----- Tagged ld64-77 + +2007-07-3 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 - ld64.xcodeproj/project.pbxproj: change install name back to ld64 - src/Options.cpp: remove support for rolling over to ld_classic - src/ld.cpp: remove support for rolling over to ld_classic + 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 @@ -24,7 +1462,7 @@ 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 @@ -36,30 +1474,30 @@ 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/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 generateion - src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a + 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/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 @@ -69,7 +1507,7 @@ ----- 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 @@ -88,11 +1526,11 @@ 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 @@ -107,15 +1545,15 @@ 2006-06-27 Nick Kledzik - src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how - dwarf debug notes are formed. + src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how + dwarf debug notes are formed. 2006-06-23 Nick Kledzik @@ -129,12 +1567,12 @@ 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 @@ -151,11 +1589,11 @@ 2006-06-15 Nick Kledzik src/rebase.cpp: fix to build in CurryWeed - ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed + ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed 2006-06-15 Nick Kledzik - Support .objc_class_name_* symbols + 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 @@ -217,7 +1655,7 @@ 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 @@ -230,10 +1668,10 @@ 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 + -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 @@ -387,6 +1825,12 @@ * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol +#ifndef __OPEN_SOURCE__ +2006-04-27 Nick Kledzik + + dyld crashes ungracefully on x86_64 when there is an internal exception + * src/MachOWriterExecutable.cpp: allow non-zero PCRELGOT addends (used by C++ eh frames) +#endif 2006-04-21 Nick Kledzik @@ -430,6 +1874,11 @@ ----- Tagged ld64-50 +#ifndef __OPEN_SOURCE__ +2006-03-29 Nick Kledzik + + * src/MachOWriterExecutable.hpp: fix x86_64 addends when -multi_module forces an external relocation +#endif 2006-03-29 Nick Kledzik @@ -481,18 +1930,62 @@ * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image +#ifndef __OPEN_SOURCE__ +2006-03-21 Nick Kledzik + + Inca ld64-45 fatal error with C++ and asm() renaming + * src/MachOReaderRelocatable.hpp: fix Reader::makeReferenceToEH(() to walk relocations to find + target of eh symbol, rather assume target name is eh symbol name less .eh + +#endif 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 +#ifndef __OPEN_SOURCE__ +2006-03-21 Nick Kledzik + + 32-bit pointer difference out of range for cxx eh frame + * src/MachOReaderRelocatable.hpp: x86_64 doesn't have anonymous non-lazy-pointers + * src/machochecker.cpp: fix validFile() for x86_64 + * unit-tests/run-all-unit-tests: add x86_64 + * unit-tests/include/common.makefile: don't add -g to all compilers + * unit-tests/test-cases/relocs-asm/relocs-asm.s: add x86_64 test cases + * unit-tests/test-cases/relocs-c/Makefile: fix to work with x86_64 + * src/ld.cpp: add hack to use i386 dylibs if x86_64 don't exist + +2006-03-19 Nick Kledzik + + ld64 crashes whenever I try to link with dylib1.o + * src/MachOReaderRelocatable.hpp: in Reader::addRelocReference() fix local relocations +#endif ----- Tagged ld64-47.1 +#ifndef __OPEN_SOURCE__ +2006-03-16 Nick Kledzik + + ld64-41 in Leopard doesn't have x86_64 support + * ld64.xcodeproj/project.pbxproj: enable x86_64 for Leopard +#endif ----- Tagged ld64-47 +#ifndef __OPEN_SOURCE__ +2006-03-14 Nick Kledzik + + * src/Architectures.hpp: redo x86_64 relocation types + * src/MachOReaderRelocatable.hpp: redo x86_64 relocation types, make some section type illegal for x86_64 + * src/MachOWriterExecutable.hpp: redo x86_64 relocation types + +2006-03-13 Nick Kledzik + + ld64 -r does not handle internal relocations for x86_64 + * src/MachOWriterExecutable.hpp: handle internal relocations in Writer::fixUpReferenceRelocatable() + and Writer::addObjectRelocs(). +#endif ----- Tagged ld64-46 @@ -526,12 +2019,24 @@ ----- Tagged ld64-45 +#ifndef __OPEN_SOURCE__ +2006-03-06 Nick Kledzik + + ld64 failed: rel32 out of range when linking a dylib + * src/MachOWriterExecutable.cpp: in Writer::fixUpReferenceFinal add (int32_t) cast +#endif 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 +#ifndef __OPEN_SOURCE__ +2006-03-06 Nick Kledzik + + x86_THREAD_STATE64_COUNT will change, ld64 must adapt + * src/MachOWriterExecutable.hpp: update ThreadsLoadCommandsAtom for new thread status layout +#endif ----- Tagged ld64-44 @@ -546,6 +2051,14 @@ ----- Tagged ld64-43 +#ifndef __OPEN_SOURCE__ +2006-03-03 Nick Kledzik + + RIP-relative offsets aren't handled properly when the instruction has immediate operands + * src/Architectures.hpp: add x86_64::kPCRel32_* + * src/MachOReaderRelocatable.hpp: generate x86_64::kPCRel32_* + * src/MachOWriterExecutable.hpp: process x86_64::kPCRel32_* +#endif 2006-03-02 Nick Kledzik diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index 835e69e..f5c4f15 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -1,1842 +1,623 @@ -.TH LD 1 "March 18, 2006" "Apple Computer, Inc." -.SH NAME -ld \- Mach object file link editor -.SH SYNOPSIS -.B ld -[ -.I "option \&..." -] [ -.I "file \&..." -] -.SH DESCRIPTION +.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 -.I ld -command combines several Mach-O (Mach object) files into one by combining like sections -in like segments from all the object files, resolving external references, and -searching libraries. In the simplest case several object -.I files -are given, and -.I ld -combines them, producing an object file which can either be executed or -become the input for a further -.I ld -run. (In the latter case, the -.B \-r -option must be given to preserve the relocation information.) Unless an output -file is specified, -.I ld -produces a file named -.BR a.out . -This file is made executable only if no errors occurred during the link editing -and there are no undefined symbols. -.SH "UNIVERSAL FILE SUPPORT" -The link editor accepts ``universal'' (multiple-architecture) input files, but -always creates a ``thin'' (single-architecture), standard Mach-O output file. -The architecture is specified using the -.B \-arch -.I " arch_type" -option. If this option is not used, -.IR ld (1) -attempts to determine the output architecture by examining the first object -file encountered on the command line. If it is a ``thin'' -file, its architecture determines that of the output file. If the first input -file is a ``universal'' file, the ``best'' architecture for the host is used. -(See the explanation of the -.B \-arch -option, below.) -.PP -The compiler driver -.IR cc (1) -handles creating universal executables by calling -.IR ld (1) -multiple times and using -.IR lipo (1) -to create a ``universal'' file from the results of the -.IR ld (1) -executions. -.SH "OUTPUT FILE LAYOUT" -.PP +.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. -.PP -Sections created from files with the -.B \-sectcreate -option will appear in the output file last. Section names for sections created -from files are not allowed to overlap with a section name in the same segment -as a section coming from an object file. Sections created from files may be in -a segment which has sections from object files and if so will be loaded at the -end of the non-zero fill sections for that segment. -.PP -If the option -.B \-seglinkedit -is specified, the segment it creates is the last segment in the output file. -.PP -The address of each segment can be specified with -.B \-segaddr, -which takes the segment's name as an argument. -The address of the first segment can alternatively be specified using -.B \-seg1addr, -in which case a segment name is not used. -Segments that do not have a specified -address will be assigned addresses in the order in which they appear -in the output file. A segment's address will be assigned -based on the ending address of the previous segment. -If the address of the -first segment has not been specified by name, -its assigned address will be -the specified (via -.BR \-seg1addr ) -or default first segment address. -If neither flag is used to specify the first segment's address, its -default address is zero -for all formats except the demand-paged executable format -.SM (MH_EXECUTE), -in which case the default first address is the value of the segment alignment. -.PP -For demand-paged executable format -.SM (MH_EXECUTE) -output files, -if none of the segments' addresses covers address zero through -the value of the segment alignment, a segment with no access protection -will be created to cover those addresses. This segment, named -.SM "``_\|_PAGEZERO''," -is created so that any attempt to dereference a NULL pointer will cause a -memory exception. -.PP -The entry point of the output file is the beginning of -the first section in the first segment (unless the -.B \-e -option is specified). -.SH STATIC ARCHIVE LIBRARIES -.PP -.I ld -supports two types of libraries: static archive libraries and dynamic shared -libraries. Searching for undefined symbols is performed differently for dynamic -shared libraries than it is for static archive libraries. The searching of -dynamic shared libraries is described later. -.PP -When a static archive library is specified as an argument to -.IR ld , -it is searched exactly once, at the -point it is encountered in the argument list. Only those members defining an unresolved external -reference, as defined by the static archive library's table of contents, -are loaded. To produce the table of contents, all static archive libraries must be processed by -.IR ranlib (1). -.PP -Generally, a static archive library does not have multiple members that define -the same symbol. For these types of libraries, the order of the members is not important, so -the table of contents can be sorted for faster link editing using the -.B \-s -option to -.IR ranlib (1). -The first member -of the static archive library is named -.SM "``\_\^\_.SYMDEF SORTED''," -which is understood to be a sorted table of contents. -.PP -If the static archive library does have multiple members that define -the same symbol, the table of contents that -.IR ranlib (1) -produces can't be sorted. Instead, it follows the order in which the members -appear in the static archive library. The link editor searches the table of -contents iteratively, loading members until no further references are -satisfied. In the unsorted case, the first member of the static archive -library is named -.SM "``\_\^\_.SYMDEF''," -which is understood to be a table of contents in -the order of the archive members. -.PP -Static archive library members can also be loaded in response to -the -.B \-ObjC -and -.B \-all_load -flags. See their descriptions below. - -.SH DYNAMIC SHARED LIBRARIES -.PP -When a dynamic shared library or an object file that was linked against a -dynamic shared library is specified as an argument to -.IR ld , -that library is placed in the dynamic shared library search list. The order of -the search list is always the same order the libraries were encountered on the -command line. When linking -flat_namespace, all dynamic libraries that the -dynamic libraries are dependent upon are added to the end of the search list. -.PP -Once the search list is constructed, the static link editor checks for undefined -symbols by simulating the way the dynamic linker will search for undefined -symbols at runtime. For each undefined symbol, the static link editor searches -each library in the search list until it finds a module that defines the symbol. -With each undefined symbol, the search starts with the first library in the -list. This is different than for static archive libraries, where each library -is searched exactly once for all undefined symbols. -.PP -The static link editor simulates dynamic linking as if all the undefined -symbols are to be bound at program launch time. The dynamic linker actually -binds undefined symbols as they are encountered during execution instead of at -program launch. However, the static link editor always produces the same linking -as the dynamic linker as long as none of the dynamic shared libraries define the -same symbol. Different linking can occur only when there is more than one -definition of a symbol and the library modules that contain the definitions for -that symbol do not define and reference exactly the same symbols. In this case, -even different executions of the same program can produce different linking -because the dynamic linker binds undefined functions as they are called, and -this affects the order in which undefined symbols are bound. Because it can -produce different dynamic linking, using dynamic shared libraries that define -the same symbols in the same program is strongly discouraged. -.PP -If a static archive library appears after a dynamic shared library on the -command line, the static library is placed in the dynamic library search list -and is searched as a dynamic library. In this way, when a dynamic library has -undefined symbols, it will cause the appropriate members of the static libraries -to be loaded into the output. Searching static libraries as dynamic libraries -can cause problems if the dynamic library later changes to reference symbols -from the static library that it did not previously reference. In this case when -the program runs, the dynamic linker will report these symbols as undefined -because the members for these symbols were not loaded into the output. - -.SH TWO-LEVEL AND FLAT NAMESPACES -.PP -Two-level and flat namespaces refer to how references to symbols in dynamic -libraries are resolved to a definition in specific dynamic library. For -two-level namespace that resolution is done at static link time when each -image (program, bundle and shared library) is built. When a program is using -images built with two-level namespace there may be different global symbols -with the same name being used by different images in the program (this is now -the default). When a program is using all flat namespace images then only one -global symbol for each global symbol name is used by all images of the program -(this was the default in MacOS X 10.0). -.PP -When creating an output file with the static link editor that links against -dynamic libraries, the references to symbols in those libraries can be recorded -at static link time to bind to a specific library definition (two-level -namespace) or left to be bound at execution time to the first library in the -search order of the program (flat namespace). A program, its dynamic libraries -and its bundles may each be either two-level or flat namespace images. The -dynamic linker will bind each image according to how it was built. -.PP -When creating an output file with the static link editor when -.B \-twolevel_namespace -is in effect (now the default) all undefined references must be satisfied at -static link time. The flags to allow undefined references, -.BI \-U symbol_name, -.BI \-undefined " warning" -and -.BI \-undefined " suppress" -can't be used. -When the environment variable -.B MACOSX_DEPLOYMENT_TARGET -is set to -.B 10.3 -or higher then -.BI \-undefined " dynamic_lookup" -can also be used. The specific library definition recorded -for each reference is the first library that has a definition as listed on the -link line. Listing an umbrella framework implies all of its sub-frameworks, -sub-umbrellas and sub-libraries. For any reference to a definition found in -an umbrella framework's sub-framework, sub-umbrella or sub-library will be -recorded as coming from the umbrella framework. Then at execution time the -dynamic linker will search that umbrella framework's sub-frameworks, -sub-umbrellas and sub-libraries for those references. -Also when two-level namespace is in effect only those frameworks listed on the -link line (and sub-frameworks, sub-umbrellas and sub-libraries of umbrella -frameworks) are searched. Other dependent libraries which are not -sub-frameworks, sub-umbrellas or sub-libraries of umbrella frameworks are not -searched. -.RS -.PP -When creating bundles (MH_BUNDLE outputs) with the static link editor when -two-level namespace is in effect (now the default) and the bundle has -references to symbols -expected to be defined in the program loading the bundle, then the -.BI \-bundle_loader " executable" -must be used. -.PP -When creating a output file with the static link editor when -.B \-flat_namespace -is in effect (the MacOS X 10.0 default) all undefined references must be -satisfied at static link time when -.BI \-undefined " error" -(the default) is used. The static -link editor checks the undefined references by searching all the libraries -listed on the link line then all dependent libraries. The undefined symbols -in the created output file are left to be resolved at execution time by the -dynamic link editor in the dynamic libraries in the search order of the program. - -.SH MULTIPLY DEFINED SYMBOLS -.PP -If there are multiply defined symbols in the object files being linked into -the output file being created this always results in a multiply defined -symbol error. -.PP -When the static link editor links symbols in from a dynamic library that result -in multiply defined symbols the handling depends on the type of name space of -output file being created and possibly the type of name space of the dynamic -library. -.PP -When the static link editor is creating a two-level namespace image and a -there is a multiply defined symbol from dynamic library then that generates a -multiply defined symbol warning (by default), where the treatment of this -warning can be changed with the -.B \-multiply_defined -flag. -.PP -When the static link editor is creating a flat namespace image and a there is -a multiply defined symbol from dynamic library, if the library is a flat -namespace image then that generates a multiply defined symbol error. If the -library is a two-level namespace image then that generates a multiply defined -symbol warning (by default), where the treatment of this warning can be changed -with the -.B \-multiply_defined -flag. - -.SH "USING THE DYNAMIC LINK EDITOR AND DYNAMIC SHARED LIBRARIES" -.PP -The option -.B \-dynamic -must be specified in order to use dynamic shared libraries (and any of the features used to implement them) and/or the dynamic link editor. -To make sure that the output is not using any features that would -require the dynamic link editor, the flag -.B \-static -can be specified. -Only one of these flags can be specified. - -.SH "LINK EDITOR DEFINED SYMBOLS" -.PP -There is a group of link editor defined symbols for the -.SM MH_EXECUTE, -.SM MH_DYLIB -and -.SM MH_PRELOAD -file types (see the header file ). Link editor symbols are -reserved; it is an error if an input object file defines such a symbol. -Only those link editor symbols that are referenced by the object file -appear in the output file's symbol table. -.PP -The link editor defined symbol `\_\^\_mh_execute_header' -(`\_mh_execute_header' in C) is reserved when the output file format is -.SM MH_EXECUTE. -This symbol is the address of the Mach header in a Mach-O executable (a -file of type -.SM MH_EXECUTE). -It does not appear in -any other Mach-O file type. It can be used to get to the addresses and -sizes of all the segments and sections in the executable. This can be done by parsing the headers -and load commands (see -.IR Mach-O (5)). -.PP -The link editor defined symbol `\_\^\_mh_dylib_header' -(`\_mh_dylib_header' in C) is reserved when the output file format is -.SM MH_DYLIB. -This symbol is the address of the Mach header in a Mach-O dynamic shared library -(a file of type -.SM MH_DYLIB) -and is a private external symbol. -It does not appear in -any other Mach-O file type. It can be used to get to the addresses and -sizes of all the segments and sections in a dynamic shared library. The -addresses, however, must have the value -.IR _dyld_get_image_vmaddr_slide (3) -added to them. -.PP -The -.SM MH_PRELOAD -file type has link editor defined symbols for the -beginning and ending of each segment, and for the -beginning and ending of each section within a segment. -These names are provided for use in a Mach-O preloaded file, -since it does not have its headers loaded as part of the first segment. -The names of the symbols for a segment's beginning and end -have the form: \_\^\_SEGNAME\_\^\_begin and \_\^\_SEGNAME\_\^\_end, -where \_\^\_SEGNAME is the name of the segment. Similarly, the symbols for -a section have the form: -\_\^\_SEGNAME\_\^\_sectname\_\^\_begin and \_\^\_SEGNAME\_\^\_sectname\_\^\_end, -where \_\^\_sectname is the name of the section in the segment \_\^\_SEGNAME. -These symbols' types are those of the section that the names refer to. -(A symbol that refers to the end of a section actually has, as its value, the beginning address of the next section, but the symbol's type is still that of the section mentioned in the symbol's name.) -.SH OPTIONS -.PP -.I Ld -understands several options. Filenames and -options that refer to libraries (such as -.B \-l -and -.BR \-framework ), -as well as options that create symbols (such as -.B \-u -and -.BR \-i ), -are position-dependent: They define the load order and affect what gets -loaded from libraries. -Some -.I ld -options overlap with compiler options. If the compiler driver -.IR cc (1) -is used to invoke -.I ld , -it maybe necessary to pass the -.IR ld (1) -options to -.IR cc (1) -using -.BR \-Wl,\-option,argument1,argument2 . - -In this release of the static link editor, 64-bit code (-arch ppc64) are processed by a separate -tool /usr/bin/ld64. Not all of the ld command line options are recognized by this tool. -The options not currently support for building 64-bit binaries are flagged -.BR "(32-bit only)" . - -The most common option is: -.TP -.BI \-o " name" -The output file is named -.IR name , -instead of -.BR a.out . - -.PP -The following flags are related to architectures: -.TP -.BI \-arch " arch_type" -Specifies the architecture, -.I arch_type, -for the output file. ``Universal'' input files that do not contain this -specified architecture are ignored. Only one -.BI \-arch " arch_type" -can be specified. See -.IR arch (3) -for the currently known -.IR arch_type s. -If -.I " arch_type" -specifies a certain implementation of an architecture (such as -.BI \-arch " m68040" -or -.BI \-arch " i486" -), the resulting object file has that specific CPU subtype, and it is an -error if any input file has a CPU subtype that will not combine to the CPU subtype -for -.IR " arch_type" . -.IP -The default output file architecture is determined by the first object file to -be linked. If it is a ``thin'' (standard Mach-O) file, or a ``universal'' file -that contains only one architecture, the output file will have the same -architecture. Otherwise, if it is a ``universal'' file -containing an architecture that would execute on the host, then the ``best'' -architecture is used, as defined by what the kernel exec(2) would select. -Otherwise, it is an error, and a -.BI \-arch " arch_type" -must be specified. -.TP -.B \-arch_multiple -This flag is used by the -.IR cc (1) -driver program when it is run with multiple -.BI \-arch " arch_type" -flags. It instructs programs like -.IR ld (1) -to precede any displayed message with a line stating -the program name, in this case -.IR ld , -and the architecture (from the -.BI \-arch " arch_type" -flag). This helps distinguish which architecture the error messages refer to. -.TP -.B \-force_cpusubtype_ALL -The -.B \-force_cpusubtype_ALL -flag causes the CPU subtype to remain the -.SM ALL -CPU subtype and not to be combined or -changed. This flag has precedence over any -.BI \-arch " arch_type" -flag for a specific implementation. -This is the default for all x86 architectures. -.PP -The following flags are related to using the dynamic link editor and/or -dynamic shared libraries (and any of the features used to implement them): -.TP -.B \-dynamic -Allows use of the features associated with dynamic link editor. The default is -.B \-dynamic. -.TP -.B \-static -Causes those features associated with dynamic link editor to be treated as -an error. (The description for the options that will cause an error if you use them in conjunction with -.B \-static -are marked with the statement "when -.B \-dynamic - is used"). -.TP -.BI \-read_only_relocs " treatment" -Specifies how relocation entries in read-only sections are to be treated when -.B \-dynamic -is used. -To get the best possible sharing, the read-only sections should not have any -relocation entries. -If they do, the dynamic linker will write on the section. -Having relocation entries appear in read-only sections is normally avoided by compiling with the option -.B \-dynamic. -But in such cases non-converted assembly code -or objects not compiled with -.B \-dynamic -relocation entries will appear in read-only sections. -The -.I treatment -can be: -.I error, -.I warning, -or -.I suppress. -Which cause the treatment of relocation entries in read-only sections as either, -errors, warnings, or suppressed messages. -The default is to treat these as errors. -.TP -.BI \-sect_diff_relocs " treatment" -Specifies how section difference relocation enries are to be treated when -.B \-dynamic -and -.B \-execute -are used. -To get the best possible code generation the compiler should not generate code -for executables (MH_EXECUTE format outputs) that have any section difference -relocation entries. The -.IR gcc (1) -compiler has the -.B \-mdynamic-no-pic -flag for generating code for executables. The default treatment is -.I suppress, -where no message is printed. The other treatments are -.I error -or -.I warning. -This option can also be specified by setting the environment variable -.SM LD_SECT_DIFF_RELOCS -to the treatment values. -.TP -.BI \-weak_reference_mismatches " treatment" -Specifies how to treat mismatches of symbol references in the the object files -being linked. Normally the all the undefined symbol references of the object -files being linked should be consistent for each undefined symbol. That is all -undefined symbols should either be weak or non-weak references. The default -treatment is -.I error, -where the link fails with an error message. The other treatments are -.I weak -or -.I non-weak, -which makes mismatched undefined symbol references either weak or non-weak -in the output, respectively. Care must be taken when using the treatment -.I weak -as the use of the non-weak symbol references in an object file may cause the -program to crash when the symbol is not present at execution time. -.TP -.B \-prebind (32-bit only) -Have the static linker, -.IR ld (1), -prebind an executable's or dynamic shared library's undefined symbols to the -addresses of the dynamic libraries it is being linked with. -This optimization can only be done if the libraries don't overlap and -no symbols are overridden. -When the resulting program is run and the same libraries are used to run the -program as when the program was linked, the dynamic linker can use the prebound -addresses. -If not, the dynamic linker undoes the prebinding and binds normally. -This option can also be specified by setting the environment variable -.SM LD_PREBIND. -If the environment variable -.SM LD_FORCE_NO_PREBIND -is set both the option -.B \-prebind -.SM LD_PREBIND -environment variable are ignore and the output is not prebound. -Or if the environment variable -.B MACOSX_DEPLOYMENT_TARGET -is set to 10.4 or greater and the output is not a split a dynamic library the -output is not prebound. -.TP -.B \-noprebind (32-bit only) -Do not have the static linker, -.IR ld (1), -prebind the output. If this is specified the environment variable -.SM LD_PREBIND -is ignored. -.TP -.B \-prebind_allow_overlap (32-bit only) -Have the static linker, -.IR ld (1), -prebind the output even if the addresses of the dynamic libraries it uses -overlap. The resulting output can then have -.IR redo_prebinding (1) -run on it to fix up the prebinding after the overlapping dynamic libraries -have been rebuilt. This option can also be specified by setting the -environment variable -.SM LD_PREBIND_ALLOW_OVERLAP. -.TP -.B \-prebind_all_twolevel_modules (32-bit only) -Have the static linker, -.IR ld (1), -mark all modules from prebound two-level namespace dynamic libraries as used -by the program even if they are not statically referenced. This can provide -improved launch time for programs like Objective-C programs that use symbols -indirectly through NIB files. This option can also be specified by setting the -environment variable -.SM LD_PREBIND_ALL_TWOLEVEL_MODULES. -.TP -.B \-noprebind_all_twolevel_modules (32-bit only) -Don't have the static linker, -.IR ld (1), -mark all modules from prebound two-level namespace dynamic libraries as used -by the program. This flag overrides the setting of the -environment variable -.SM LD_PREBIND_ALL_TWOLEVEL_MODULES. -.TP -.B \-nofixprebinding (32-bit only) -Have the static linker, -.IR ld (1), -mark the executable so that the dynamic linker will never notify the prebinding -agent if this launched and its prebinding is out of date. This is used when -building the prebinding agent itself. -.PP -The following flags are related to libraries: -.TP -.BI \-l x -This -option is an abbreviation for the library name -.RI `lib x .a', -where -.I x -is a string. -If -.B \-dynamic -is specified the abbreviation for the library name is first search as -.RI `lib x .dylib' -and then -.RI `lib x .a' -is searched for. -.I ld -searches for libraries first in any directories -specified with -.B \-L -options, then in any directories specified in the colon separated set of paths -in the environment variable LD_LIBRARY_PATH, then the standard directories -.BR /lib , -.BR /usr/lib , -and -.BR "/usr/local/lib" . -A library is searched when its name is encountered, -so the placement of the -.B \-l -flag is significant. If string -.I x -is of the form -.IR x .o, -then that file is searched for in the same places, but without prepending -`lib' or appending `.a' or `.dylib' to the filename. -.TP -.BI \-weak-l x -This is the same as the -.BI \-l x -but forces the library and all references to it to be marked as weak imports. -Care must be taken when using this as the use of the non-weak symbol references -in an object file may cause the program to crash when the symbol or library is -not present at execution time. -.TP -.BI \-weak_library " file_name_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. -Care must be taken when using this as the use of the non-weak symbol references -in an object file may cause the program to crash when the symbol or library is -not present at execution time. -.TP -.BI \-L dir -Add -.I dir -to the list of directories in which to search for libraries. -Directories specified with -.B \-L -are searched before the standard directories. -.TP -.B \-Z -Do not search the standard directories when searching for libraries. -.TP -.BI "\-syslibroot " rootdir " (32-bit only)" -Prepend -.I rootdir -to the standard directories when searching for libraries or frameworks. -.TP -.B \-search_paths_first -By default when the -.B \-dynamic -flag is in effect, the -.BI \-l x -and -.BI \-weak-l x -options first search for a file of the form -.RI `lib x .dylib' -in each directory in the library search path, then a file of the form -.RI `lib x .a' -is searched for in the library search paths. -This option changes it so that in each path -.RI `lib x .dylib' -is searched for then -.RI `lib x .a' -before the next path in the library search path is searched. -.TP -.BI "\-framework " name[,suffix] -Specifies a framework to link against. Frameworks are dynamic shared libraries, -but they are stored in different locations, and therefore must be searched for -differently. When this option is specified, -.I ld -searches for framework `\fIname\fR.framework/\fIname\fR' -first in any directories -specified with the -.B \-F -option, then in the standard framework directories -.BR /Library/Frameworks , -.BR /Network/Library/Frameworks , -and -.BR "/System/Library/Frameworks" . -The placement of the -.B \-framework -option is significant, as it determines when and how the framework is searched. -If the optional suffix is specified the framework is first searched for the -name with the suffix and then without. -.TP -.BI "\-weak_framework " name[,suffix] -This is the same as the -.BI "\-framework " name[,suffix] -but forces the framework and all references to it to be marked as weak imports. -Care must be taken when using this as the use of the non-weak symbol references -in an object file may cause the program to crash when the symbol or framework is -not present at execution time. -.TP -.BI \-F dir -Add -.I dir -to the list of directories in which to search for frameworks. -Directories specified with -.B \-F -are searched before the standard framework directories. -.TP -.B \-ObjC -Loads all members of static archive libraries that define an Objective C class or a category. This option does not apply to dynamic shared libraries. -.TP -.B \-all_load +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. +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 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). +.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 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. -This option does not apply to dynamic shared -libraries. -.TP -.BI \-dylib_file " install_name:file_name" (32-bit only) -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. -.I install_name -specifies the path where the library normally resides. -.I 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: -\fB\-dylib_file /lib/libsys_s.A.dylib:/me/lib/libsys_s.A.dylib\fR. -.TP -.BI \-executable_path " path_name" (32-bit only) -Specifies that -.I path_name -is used to replace -.I @executable_path -for dependent libraries. - -.PP -The following options specify the output file format (the file type): -.TP -.B "\-execute" -Produce a Mach-O demand-paged executable format file. The headers are placed -in the first segment, and all segments are padded to the segment alignment. -This has a file type of -.SM MH_EXECUTE. -This is the default. If no segment address is specified at address zero, a -segment with no protection (no read, write, or execute permission) is created -at address zero. -This segment, whose size is that of the segment -alignment, is named -.SM ``_\|_PAGEZERO''. -This option was previously named -.BR "\-Mach" , -which will continue to be recognized. -.TP -.B \-object (32-bit only) -Produce a Mach-O file in the relocatable object file format that is -intended for execution. This differs from using the -.B \-r -option in that it defines common symbols, does not allow undefined symbols and -does not preserve relocation entries. This has a file type of -.SM MH_OBJECT. -In this format all sections are placed in one unnamed segment with all -protections (read, write, execute) allowed on that segment. This is intended -for extremely small programs that would otherwise be large due to segment -padding. In this format, and all -.SM non-MH_EXECUTE -formats, the link editor -defined symbol ``\_\^\_mh_execute_header'' is not defined since the headers are -not part of the segment. This format file can't be used with the dynamic -linker. -.TP -.B \-preload (32-bit only) -Produce a Mach-O preloaded executable format file. The headers are not placed -in any segment. All sections are placed in their proper segments and they are -padded to the segment alignment. This has a file type of -.SM MH_PRELOAD. -This option was previously -.BR "\-p" , -which will continue to be recognized. -.TP -.B "\-dylib" -Produce a Mach-O dynamically linked shared library format file. The headers are -placed in the first segment. All sections are placed in their proper segments -and they are padded to the segment alignment. This has a file type of -.SM MH_DYLIB. -This option is used by -.IR libtool (1) -when its -.B \-dynamic -option is specified. -.TP -.B "\-bundle" -Produce a Mach-O bundle format file. The headers are placed in the first -segment. All sections are placed in their proper segments -and they are padded to the segment alignment. This has a file type of -.SM MH_BUNDLE. -.TP -.B "\-dylinker" -Produces a Mach-O dynamic link editor format file. The headers are placed in the -first segment. All sections are placed in their proper segments, and they are -padded to the segment alignment. This has a file type of -.SM MH_DYLINKER. -.TP -.B \-fvmlib (32-bit only) -Produce a Mach-O fixed VM shared library format file. The headers are placed -in the first segment but the first section in that segment will be placed on -the next segment alignment boundary in that segment. All sections are placed -in their proper segments and they are padded to the segment alignment. -This has a file type of -.SM MH_FVMLIB. - -.PP -The following flags affect the contents of the output file: -.TP -.B \-r -Save the relocation information in the output file -so that it can be the subject of another -.I ld -run. The resulting file type is a Mach-O relocatable file -.SM (MH_OBJECT) -if not otherwise specified. -This flag also prevents final definitions from being -given to common symbols, -and suppresses the `undefined symbol' diagnostics. -.TP -.B \-d (32-bit only) -Force definition of common storage even if the -.B \-r -option is present. This option also forces link editor defined symbols to be defined. -This option is assumed when there is a dynamic link editor load command in the input -and -.B \-r -is not specified. - -.PP -The following flags support segment specifications: -.TP -.BI "\-segalign" " value" " (32-bit only)" -Specifies the segment alignment. -.I value -is a hexadecimal number that must be an integral power of 2. -The default is the target pagesize (currently 1000 hex for the PowerPC and -i386). -.TP -.BI "\-seg1addr" " addr" -Specifies the starting address of the first segment in the output file. -.I addr -is a hexadecimal number and must be a multiple of the segment alignment. -This option can also be specified as -.B "\-image_base." -.TP -.BI "\-segaddr" " name addr" " (32-bit only)" -Specifies the starting address of the segment named -.I name -to be -.I addr. -The address must be a hexadecimal number that is a multiple of the segment alignment. -.TP -.BI "\-segs_read_only_addr" " addr" " (32-bit only)" -Specifies the starting address of the read-only segments in a dynamic shared -library. When this option is used the dynamic shared library is built such -that the read-only and read-write segments are split into separate address -ranges. By default the read-write segments are 256meg (0x10000000) after -the read-only segments. -.I addr -is a hexadecimal number and must be a multiple of the segment alignment. -.TP -.BI "\-segs_read_write_addr" " addr" " (32-bit only)" -Specifies the starting address of the read-write segments in a dynamic shared -library. When this option is used the -.B \-segs_read_only_addr -must also be used (see above). -.I addr -is a hexadecimal number and must be a multiple of the segment alignment. -.TP -.BI "\-seg_addr_table" " filename" " (32-bit only)" -For dynamic shared libraries the -.B "\-seg1addr" -or the pair of -.B "\-segs_read_only_addr" -and -.B "\-segs_read_write_addr" -are specified by an entry in the segment address table in -.I filename -that matches the install name of the library. -The entries in the table are lines containing either a single hex address and an -install name or two hex addresses and an install name. In the first form the -single hex address is used as the -.B "\-seg1addr". -In the second form the first address is used as the -.B "\-segs_read_only_addr" -address and the second address is used as the -.B "\-segs_read_write_addr" -address. -This option can also be specified by setting the environment variable -.SM LD_SEG_ADDR_TABLE. -If the environment variable is set then any -.BR "\-seg1addr" , -.BR "\-segs_read_only_addr" , -.B "\-segs_read_write_addr" -and -.B "\-seg_addr_table" -options are ignored and a warning is printed. -.TP -.BI "\-seg_addr_table_filename" " pathname" " (32-bit only)" -Use -.B pathname -instead of the install name of the library for matching an entry in the segment -address table. -.TP -.BI "\-segprot" " name max init" " (32-bit only)" -Specifies the maximum and initial virtual memory protection of the named -segment, -.I name, -to be -.I max -and -.I init -,respectively. The values for -.I max -and -.I init -are any combination of the characters `r' (for read), `w' (for write), -`x' (for execute) and '\-' (no access). The default is `rwx' for the maximum -protection for all segments for PowerPC architecures and `rw` for the all Intel -architecures. -The default for the initial protection for all segments is `rw' unless the -segment contains a section which contains some machine instructions, in which -case the default for the initial protection is `rwx' (and for Intel -architecures it also sets the maximum protection to `rwx' in this case). -The default for the initial protection for the -.SM "``_\|_TEXT''" -segment is `rx' (not writable). -.TP -.B \-seglinkedit (32-bit only) -Create the link edit segment, named -.SM "``_\|_LINKEDIT''" -(this is the default). -This segment contains all the link edit information (relocation information, -symbol table, string table, etc.) in the object file. If the segment protection -for this segment is not specified, the initial protection is not writable. -This can only be specified when the output file type is not -.SM MH_OBJECT -and -.SM MH_PRELOAD -output file types. To get at the contents of this section, the Mach header -and load commands must be parsed from the link editor defined symbols like -`\_\^\_mh_execute_header' (see -.IR Mach-O (5)). -.TP -.B \-noseglinkedit (32-bit only) -Do not create the link edit segment (see -.B \-seglinkedit -above). -.TP -.BI "\-pagezero_size" " value" -Specifies the segment size of _\|_PAGEZERO to be of size -.IR value , -where -.I value -is a hexadecimal number rounded to the segment alignment. -The default is the target pagesize (currently, 1000 hexadecimal for the PowerPC -and for i386). -.TP -.BI "\-stack_addr" " value" -Specifies the initial address of the stack pointer -.IR value , -where -.I 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 -.B \-stack_size -is specified and -.B \-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. -.TP -.BI "\-stack_size" " value" -Specifies the size of the stack segment -.IR value , -where -.I 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 -.B \-stack_addr -is specified and -.B \-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 . -.TP -.B \-allow_stack_execute -Marks executable so that all stacks in the task will be given stack execution -privilege. This includes pthread stacks. - -.PP -The following flags support section specifications: -.TP -.BI "\-sectcreate" " segname sectname file" -The section -.I sectname -in the segment -.I segname -is created from the contents of -.I file. -The combination of -.I segname -and -.I sectname -must be unique; there cannot already be a section -.I (segname,sectname) -in any input object file. -This option was previously called -.BR "\-segcreate" , -which will continue to be recognized. -.TP -.BI "\-sectalign" " segname sectname value" -The section named -.I sectname -in the segment -.I segname -will have its alignment set to -.IR value , -where -.I value -is a hexadecimal number that must be an integral power of 2. -This can be used to set the alignment of a section created from a file, or to -increase the alignment of a section from an object file, or to set the maximum -alignment of the -.SM (_\|_DATA,_\|_common) -section, where common symbols are defined -by the link editor. Setting the alignment of a literal section causes the -individual literals to be aligned on that boundary. If the section -alignment is not specified by a section header in an object file or on the -command line, it defaults to 10 (hex), indicating 16-byte alignment. -.TP -.BI "\-sectorder" " segname sectname orderfile" (32-bit only) -The section -.I sectname -in the segment -.I segname -of the input files will be broken up into blocks associated with -symbols in the section. The output section will be created by ordering -the blocks as specified by the lines in the -.I orderfile. -These blocks are aligned to the output file's section alignment for this -section. Any section can be ordered in the output file except symbol pointer and symbol stub sections. -.IP -For non-literal sections, each line of the -.I orderfile -contains an object name and a symbol name, separated by a single colon (':'). -Lines that start with # are ignored and treated as comments. -If the object file is -in an archive, the archive name, followed by a single colon, must precede the -object file name. The object file names and archive names should be exactly the -names as seen by the link editor, but if not, the link editor attempts to match -up the names the best it can. -For non-literal sections, the easiest way to generate an order file is -with the ``\f3\-jonls +\f2segname sectname\f1'' options to -.IR nm (1). -.IP -The format of the -.I orderfile -for literal sections is specific to each type of literal section. For C -string literal sections, each line of the order file contains one literal C -string, which may include ANSI C escape sequences. For four-byte literal -sections, the order file format is one 32-bit hex number with a leading 0x -per -line, with the rest of the line treated as a comment. For eight-byte literal -sections, the order file has two 32-bit hex numbers per line; each number -has a leading 0x, the two numbers are separated by white -space, and the rest of the line is treated as a comment. -For literal pointer sections, the lines in the order file represent -pointers, one per line. A literal pointer is represented by the name of -the segment that contains the literal being pointed to, followed by the -section name, followed by the literal. These three strings are separated -by colons with no extra white space. -For all the literal sections, each line in the the order file is simply entered -into the literal section and will appear in the output file in the same order -as in the -order file. There is no check to see whether the literal is present -in the loaded objects. -For literal sections, the easiest way to generate an order file is with -the ``\f3\-X \-v \-s \f2segname sectname\f1'' options to -.IR otool (1). -.TP -.B \-sectorder_detail (32-bit only) -When using the -.B \-sectorder -option, any pairs of object file names and symbol names that are found in -the loaded objects, but not specified in the -.IR orderfile , -are placed last in the output file's section. These pairs are ordered by -object file (as the filenames appear -on the command line), with the different symbols from a given object -file being ordered by -increasing symbol address (that is, the order -in which the symbols occurred in the object file, -not their order in the symbol table). By default, the link editor displays a summary -that simply shows the number -of symbol names found in the loaded objects but not in the -.IR orderfile , -as well as the number of symbol names listed in the -.I orderfile -but not found in the loaded objects. (The summary is omitted if both values -are zero.) To instead produce a detailed list of these symbols, use the -.B \-sectorder_detail -flag. If an object file-symbol name pair is listed multiple times, a -warning is generated, and the first occurrence is used. -.TP -.BI "\-sectobjectsymbols" " segname sectname" " (32-bit only)" -This causes the link editor to generate local symbols in the section -.I sectname -in the segment -.IR segname . -Each object file that has one of these sections will have a local -symbol created -whose name is that of the object file, or of the member of the archive. -The symbol's value will be the first address where that object file's section -was loaded. The symbol has the type N_SECT and its section number is the -the same as that of the section -.I (segname,sectname) -in the output file. -This symbol will be placed in the symbol table just before all other local -symbols -for the object file. This feature is typically used where the section is -.SM (\_\^\_TEXT,\_\^\_text), -in order to help the debugger debug object files produced by old versions of -the compiler or by non-Apple compilers. - -.PP -The following flags are related to name spaces: -.TP -.B \-twolevel_namespace -Specifies the output to be built as a two-level namespace image. -This option can also be specified by setting the environment variable -.SM LD_TWOLEVEL_NAMESPACE. -This is the default. -.TP -.B \-flat_namespace -Specifies the output to be built as a flat namespace image. -This is not the default (but was the default in MacOS X 10.0). -.TP -.B \-force_flat_namespace -Specifies the executable output to be built and executed treating all its -dynamic libraries as flat namespace images. This marks the executable so that -the dynamic link editor treats all dynamic libraries as flat namespace -images when the program is executed. -.TP -.BI \-bundle_loader " executable" " (32-bit only)" -This specifies the -.I executable -that will be loading the bundle output file being linked. Undefined symbols -from the bundle are checked against the specified executable like it was one of -the dynamic libraries the bundle was linked with. If the bundle being created -with -.B \-twolevel_namespace -in effect then the searching of the executable for -symbols is based on the placement of the -.B \-bundle_loader -flag relative to the dynamic libraries. If the the bundle being created with -.B \-flat_namespace -then the searching of the executable is done before all dynamic libraries. -.TP -.B \-private_bundle (32-bit only) -This allows symbols defined in the output to also be defined in executable in -the -.B \-bundle_loader -argument -when -.B \-flat_namespace -is in effect. -This implies that the bundle output file being created is going to be loaded by -the executable with the -.B NSLINKMODULE_OPTION_PRIVATE -option to -.IR NSLinkModule (3). -.TP -.B \-twolevel_namespace_hints (32-bit only) -Specifies to create the output with the two-level namespace hints table to be -used by the dynamic linker. This is the default except when the -.B \-bundle -flag is specified. If this is used when the -.B \-bundle -flag is specified the bundle will fail to load on a MacOS X 10.0 system with a -malformed object error. -.TP -.BI \-multiply_defined " treatment" " (32-bit only)" -Specifies how multiply defined symbols in dynamic libraries when -.B \-twolevel_namespace -is in effect are to be treated. -.I treatment -can be: -.I error, -.I warning, -or -.I suppress. -Which cause the treatment of multiply defined symbols in dynamic libraries -as either, errors, warnings, or suppresses the checking of multiply symbols -from dynamic libraries when -.B \-twolevel_namespace -is in effect. -The default is to treat multiply defined symbols in dynamic libraries as -warnings when -.B \-twolevel_namespace -is in effect. -.TP -.BI \-multiply_defined_unused " treatment" " (32-bit only)" -Specifies how unused multiply defined symbols in dynamic libraries when -.B \-twolevel_namespace -is in effect are to be treated. -An unused multiply defined symbol is one in which there is a symbol defined in -the output that is also defined in the dynamic libraries the output is linked -with but the symbol in the dynamic library is not used by any reference in the -output. -.I treatment -can be: -.I error, -.I warning, -or -.I suppress. -The default for unused multiply defined symbols is to suppress these messages. -.TP -.B -nomultidefs (32-bit only) -specifying this flag marks the umbrella being created such that the dynamic -linker is guaranteed that no multiple definitions of symbols in the umbrella's -sub-images will ever exist. This allows the dynamic linker to always use the -two-level namespace lookup hints even if the timestamps of the sub-images -do not match. This flag implies -.BI \-multiply_defined " error". - -.PP -The following flags are related to symbols. These flags' arguments -are external symbols whose names have `_' prepended to the C, -.SM FORTRAN, -or Pascal variable name. -.TP -.BI \-y sym " (32-bit only)" -Display each file in which -.I sym -appears, its type, and whether the file defines or references it. Any -multiply defined symbols are automatically -traced. Like most of the other symbol-related flags, -.B \-y -takes only one argument; the flag may be specified more than once in the -command line to trace more than one symbol. -.TP -.BI \-Y " number" " (32-bit only)" -For the first -.I number -undefined symbols, displays each file in which the symbol appears, its type and whether the file defines or references it (that is, the same style of output produced by the -.B \-y -option). To keep the output manageable, this option displays at most -.I number -references. -.TP -.B \-keep_private_externs -Don't turn private external symbols into static symbols, but rather leave them -as private external in the resulting output file. -.TP -.B \-m (32-bit only) -Don't treat multiply defined symbols from the linked objects as a hard error; -instead, simply print a warning. The first linked object defining such a symbol -is used for linking; its value is used for the symbol in the symbol table. The -code and data for all such symbols are copied into the output. The duplicate -symbols other than the first symbol may still end up being used in the resulting -output file through local references. This can still produce a resulting output -file that is in error. This flag's use is strongly discouraged! -.TP -.B \-whyload (32-bit only) -Indicate why each member of a library is loaded. In other words, indicate -which currently undefined symbol is being resolved, causing that -member to be loaded. This in combination with the above -.BI \-y sym -flag can help determine exactly why a link edit is failing due to multiply -defined symbols. -.B -.TP -.BI \-u " sym" -Enter the argument -.I sym -into the symbol table as an undefined symbol. This is useful -for loading wholly from a library, since initially the symbol -table is empty and an unresolved reference is needed -to force the loading of the first object file. -.TP -.BI \-e " sym" -The argument -.I sym -is taken to be the symbol name of the entry point of -the resulting file. By default, the entry point is the address of the -first section in the first segment. -.TP -.BI \-i definition:indirect " (32-bit only)" -Create an indirect symbol for the symbol name -.I definition -which is defined to be the same as the symbol name -.I indirect -(which is taken to be undefined). When a definition of the symbol named -.I indirect -is linked, both symbols will take on the defined type and value. -.IP -This option overlaps with a compiler option. -If you use the compiler driver -.IR cc (1) -to invoke \fIld\fR, -invoke this option in this way: -.BI \-Wl,\-i definition:indirect. - -.TP -.BI \-undefined " treatment" -Specifies how undefined symbols are to be treated. -.I treatment -can be: -.I error, -.I warning, -or -.I suppress. -Which cause the treatment of undefined symbols as either, errors, warnings, or -suppresses the checking of undefined symbols. -The default is to treat undefined symbols as errors. -When the environment variable -.B MACOSX_DEPLOYMENT_TARGET -is set to -.B 10.3 -or higher then -.BI \-undefined " dynamic_lookup" -can also be used to allow any undefined symbols to be looked up dynamically at -runtime. Use of a binary built with this flag requires a system with a dynamic -linker from Mac OS X 10.3 or later. -The flag -.BI \-undefined " define_a_way" -can also be used to cause the static linker to create a private definition for -all undefined symbols. This flag should only be used if it is known that the -undefined symbols are not referenced as any use of them may cause a crash. -.TP -.BI \-U " sym" -Allow the symbol -.I sym -to be undefined, even if the -.B \-r -flag is not given. Produce an executable file if the only undefined -symbols are those specified with -.BR \-U. -.IP -This option overlaps with a compiler option. -If you use the compiler driver -.IR cc (1) -to invoke \fIld\fR, -invoke this option in this way: -.BI \-Wl,\-U, sym. -.TP -.B \-bind_at_load -Causes the output file to be marked such that the dynamic linker will bind all -undefined references when the file is loaded or launched. -.TP -.BI \-commons " treatment" " (64-bit only)" -Specifies how common symbols (tentative defintions) from object files interact with dynamic libraries. -.I treatment -can be: -.I ignore_dylibs, -.I use_dylibs, -or -.I error. -The default is ignore_dylibs -which means the static linker will use a common defintion from an object file even if a true definition -exisits in a dynamic library. If you want your code to use a dynamic library definition, then add -the extern keyword to your tentative definition (e.g. change -.I int foo; -to -.I extern int foo; -). The treatment use_dylibs means a definition form a dynamic library should override a common symbol -in an object file. Note, the 32-bit linker always uses this treatment. -The treatment error means the linker should abort whenever if finds a common symbol in -an object file and an external definition with the same name in a dynamic library. -.TP -.B \-warn_commons (64-bit only) -Causes the static linker to write a diagnostic line about how common symbols were processed. This is -useful for debugging problems with common symbols. - -.PP -The following flags are related to stripping link edit information. -This information can also be removed by -.IR strip (1), -which uses the same options. (The -exception is the -.B \-s -flag below, but this is the same as -.IR strip (1) -with no arguments.) -The following flags are listed in decreasing level of stripping. -.TP -.B \-s (32-bit only) -Completely strip the output; that is, remove the symbol table -and relocation information. -.TP -.B \-x (32-bit only) -Strips the non-global symbols; only saves external symbols. -.IP -This option overlaps with a compiler option. -If you use the compiler driver -.IR cc (1) -to invoke \fIld\fR, -invoke this option in this way: -.B \-Wl,\-x. -.TP -.B \-S (32-bit only) -Strip debugging symbols; only save local and global symbols. -.TP -.B \-X (32-bit only) -Strip local symbols whose names begin with `L'; save all other symbols. -(The compiler and assembler currently strip these internally-generated -labels by default, so they generally do not appear in object files -seen by the link editor.) -.TP -.B \-Sp -Strip, edit and add debugging symbols so the debugger can used most of the -debugging symbols from the object files. -.TP -.B \-Si (32-bit only) -Strip duplicate debugging symbols from include files. This is -the default. -.TP -.B \-b (32-bit only) -Strip the base file's symbols from the output file. (The base file -is given as the argument to the -.B \-A -option.) -.IP -This option overlaps with a compiler option. -If you use the compiler driver -.IR cc (1) -to invoke \fIld\fR, -invoke this option in this way: -.B \-Wl,\-b. -.TP -.B \-Sn (32-bit only) -Don't strip any symbols. -.TP -.BI \-exported_symbols_list " filename" -The specified -.I filename -contains lists 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 -.I __private_extern__ -and will not be global in the output file. The symbol names listed in -.I 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. -.TP -.BI \-unexported_symbols_list " filename" -The specified -.I filename -contains lists 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 -.I __private_extern__ -and will not be global in the output file. The symbol names listed in -.I 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. -.TP -.BI \-no_uuid -Do not emit an LC_UUID load command in the linked output file. - -.TP -.B -dead_strip (32-bit only) -Remove blocks of code and data that are unreachable by the entry point or -exported symbols. -.TP -.B -no_dead_strip_inits_and_terms (32-bit only) -When specified along with -.B -dead_strip -cause all constructors and destructors to never be dead stripped. - -.PP -The remaining options are infrequently used: -.TP -.B \-v -Print the version of the linker. -.TP -.B \-w (32-bit only) -Suppresses all warning messages. -.TP -.B \-no_arch_warnings -Suppresses warning messages about files that have the wrong architecture for the -.B \-arch -flag. -.TP -.B \-arch_errors_fatal (32-bit only) -Cause the errors having to do with files that have the wrong architecture to be -fatal and stop the link editor. -.TP -.B \-M (32-bit only) -Produce a load map, listing all the segments and sections. The list -includes the address where each input file's section appears in the -output file, as well as the section's size. -.IP -This option overlaps with a compiler option. -If you use the compiler driver -.IR cc (1) -to invoke \fIld\fR, -invoke this option in this way: -.B \-Wl,\-M. -.TP -.B \-whatsloaded (32-bit only) -Display a single line listing each object file that is -loaded. Names of objects in archives have the form libfoo.a(bar.o). -.TP -.BI \-filelist " listfile[,dirname]" -Specifies that the linker should link the files listed in -.I listfile . -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, -.I dirname +.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. -.TP -.BI "\-headerpad" " value" -Specifies the minimum amount of space ("padding") following -the headers for the -.SM MH_EXECUTE -format and all output file types with the dynamic linker. -.I value -is a hexadecimal number. -When a segment's size is rounded up to the segment alignment, there -is extra space left over, which is placed between the headers and the sections, rather than at the end of the segment. The -.B headerpad -option specifies the minimum size of this padding, -which can be useful if the headers will be altered later. -The default value is the larger of 2 * sizeof(struct section) so the program -/usr/bin/objcunique can always add two section headers, or if the output is an -MH_EXECUTE filetype and -.B \-prebind -is specified 3 times the size of the LC_PREBOUND_DYLIB load commands. -The actual amount of pad will be as large as the amount of the first -segment's round-off. -(That is, take the total size of the first segments' -headers and non-zerofill sections, round this size -up to the segment alignment, -and use the difference between the rounded -and unrounded sizes as the minimum amount of padding.) -.TP -.B \-headerpad_max_install_names (32-bit only) -Add to the header padding enough space to allow changing all dynamic shared -library paths recorded in the output file to be changed to MAXPATHLEN in length. -.TP -.B \-t -Trace the progress of the link editor; display the name of each file -that is -loaded as it is processed in the first and second pass of the link -editor. -.TP -.BI \-A " basefile" " (32-bit only)" -Incremental loading: linking is to be done in a manner -that lets the resulting object be read into an already executing -program, the -.IR basefile . -.I basefile -is the name of a file whose symbol table will be taken as a basis -on which to define additional symbols. -Only newly linked material will be entered into the -.BR a.out -file, but the new symbol table will reflect -every symbol defined in the base file and the newly linked files. -Option(s) to specify the addresses of the segments are typically -needed, since -the default addresses tend to overlap with the -.I basefile. -The default format of the object file is -.SM MH_OBJECT. -Note: It is strongly recommended that this option NOT be used, -because the dyld package described in -.IR dyld (3) -is a much easier alternative. -.TP -.BI \-dylib_install_name " name" -For dynamic shared library files, specifies the name of the file -the library will be installed in for programs that use it. If this is not -specified, the name specified in the -.BI \-o " name" -option will be used. -This option is used as the -.IR libtool (1) -.BI \-install_name " name" -option when its -.B \-dynamic -option is specified. -.TP -.BI \-umbrella " framework_name" -Specifies this is a subframework where -.I framework_name -is the name of the umbrella framework this subframework is a part of. Where -.I framework_name -is the same as the argument to the -.BI \-framework " framework_name" -option. This subframework can then only be linked into the umbrella framework -with the same -.I framework_name -or another subframework with the same umbrella framework name. Any other -attempt to statically link this subframework directly will result in an error -stating to link with the umbrella framework instead. When building the umbrella -framework that uses this subframework no additional options are required. -However the install name of the umbrella framework, required to be specified -with -.BR \-dylib_install_name , -must have the proper format for an install name of a framework for the -.I framework_name -of the umbrella framework to be determined. -.TP -.BI \-allowable_client " client_name" " (32-bit only)" -Specifies that for this subframework the -.I client_name -can link with this subframework without error even though it is not part of -the umbrella framework that this subframework is part of. The -.I client_name -can be another framework name or a name used by bundles (see the -.BI \-client_name " client_name" -option below). -.TP -.BI \-client_name " client_name" " (32-bit only)" -Specifies the -.I client_name -of a bundle for checking of allowable clients of subframeworks (see the -.BI \-allowable_client " client_name" -option above). -.TP -.BI \-sub_umbrella " framework_name" -Specifies that the -.I framework_name -being linked by a dynamic library is to be treated as one of the -subframeworks with respect to twolevel namespace. -.TP -.BI \-sub_library " library_name" -Specifies that the -.I library_name -being linked by a dynamic library is to be treated as one of the -sublibraries with respect to twolevel namespace. For example the -.I library_name -for -.I /usr/lib/libobjc_profile.A.dylib -would be -.I libobjc. -.TP -.BI \-init " sym" +.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. +.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 -.I sym -is taken to be the symbol name of the dynamic shared library initialization -routine. If any module is used from the dynamic library the library -initialization routine is called before any symbol is used from the library -including C++ static initializers (and #pragma CALL_ON_LOAD routines). -.TP -.B \-run_init_lazily (32-bit only) +.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/. +.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. +.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. +.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. -.TP -.BI \-dylib_compatibility_version " number" -For dynamic shared library files, this specifies the compatibility version number -of the library. When a library is used by a program, 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 -.I number -is -.I X[.Y[.Z]] -where -.I X -must be a positive non-zero number less than or equal to 65535, and -.I .Y -and -.I .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 used as the -.IR libtool (1) -.BI \-compatibility_version " number" -option -when its -.B \-dynamic -option is set. -.TP -.BI \-dylib_current_version " number" -For dynamic shared library files, 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 -.I number -is -.I X[.Y[.Z]] -where -.I X -must be a positive non-zero number less than or equal to 65535, and -.I .Y -and -.I .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 used as the -.IR libtool (1) -.BI \-current_version " number" -option when its -.B \-dynamic -option is set. -.TP -.BI \-single_module -When building a dynamic library build the library so that it contains only -one module. -.TP -.BI \-multi_module (32-bit only) -When building a dynamic library build the library so that it contains one -module for each object file linked in. This is the default. -.TP -.BI \-dylinker_install_name " name" -For dynamic link editor files, specifies the name of the file -the dynamic link editor will be installed in for programs that use it. -.TP -.BI \-macosx_version_min " version" -This overrides the -.B MACOSX_DEPLOYMENT_TARGET -environment variable (see below). Unlike other linker options, this one may -be specified multiple times; only the last occurrence is effective. -.PP -The following environment variable is used to control the use of incompatible -features in the output with respect to Mac OS X releases. -.TP -.B MACOSX_DEPLOYMENT_TARGET -This is set to indicate the oldest Mac OS X version that that the output is to -be used on. When this is set to a release that is older than the current -release features that are incompatible with that release will be disabled. If -a feature is seen in the input that can't be in the output due to this setting -a warning is issued. The current allowable values for this are -.B 10.1, -.B 10.2 -.B 10.3, -and -.B 10.4 -with the default being -.B 10.4 -for the i386 architecture and -.B 10.1 -for all other architectures. -.PP -The following environment variables are used by Apple's Build and Integration -team: -.TP -.B LD_TRACE_ARCHIVES -When this is set it causes a message of the form ``[Logging for XBS] -Used static archive: -.I filename'' -for each static archive that has members linked into the output. -.TP -.B LD_TRACE_DYLIBS -When this is set it causes a message of the form ``[Logging for XBS] -Used dynamic library: -.I filename'' -for each dynamic library linked into the output. -.TP -.B RC_TRACE_PREBINDING_DISABLED -When this is set it causes a message of the form ``[Logging for XBS -prebinding disabled for -.I filename -because -.I reason''. -Where -.I filename -is the value of the -.B \-final_output -argument if specified or the value of the -.B \-o -argument. -.TP -.BI \-final_output " filename" -The argument -.I filename -is used in the above message when RC_TRACE_PREBINDING_DISABLED is set. -.TP -.B LD_TRACE_FILE -When this is set, messages displayed due to the -.B LD_TRACE_ARCHIVES -, -.B LD_TRACE_DYLIBS -, and -.B LD_TRACE_PREBINDING_DISABLED -environment variables are printed to the file whose path is specified -by this variable instead of stdout. -.TP -.B LD_SPLITSEGS_NEW_LIBRARIES -When set and -.B MACOSX_DEPLOYMENT_TARGET -is set to 10.4 or greater and the output is a dynamic library, and if the -install name of the library is not listed the segment address table, and if the -environment variable -.B LD_UNPREBOUND_LIBRARIES -is set with a file name with a list of library install names and the install -name is not listed, then this is built as a split shared library. - -.PP -Options available in early versions of the Mach-O link editor -may no longer be supported. - -.SH FILES -.ta \w'/Network/Library/Frameworks/*.framework/*\ \ 'u -/lib/lib*.{a,dylib} libraries -.br -/usr/lib/lib*.{a,dylib} -.br -/usr/local/lib/lib*.{a,dylib} -.br -/Library/Frameworks/*.framework/* framework libraries -.br -/Network/Library/Frameworks/*.framework/* framework libraries -.br -/System/Library/Frameworks/*.framework/* framework libraries -.br -a.out output file -.SH "SEE ALSO" -as(1), ar(1), cc(1), libtool(1), ranlib(1), nm(1), otool(1) lipo(1), -arch(3), dyld(3), Mach-O(5), strip(1), redo_prebinding(1) +.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.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index a468b0d..e8a771c 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -39,7 +39,7 @@ /* 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 */; }; - F97288E707D277570031794D /* SectCreate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97288E607D277570031794D /* SectCreate.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 */; }; @@ -120,6 +120,7 @@ dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( + F97F5029070D0BB200B9FCD7 /* ld.1 in CopyFiles */, F9FCC3F20A54A75600CEB866 /* ld64.1 in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; @@ -137,8 +138,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3DA587190ACC53BE0015C432 /* LLVMReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LLVMReader.hpp; path = src/LLVMReader.hpp; sourceTree = ""; }; C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; - F9023C3906D5A23E001BBF46 /* ld64 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld64; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; @@ -151,9 +153,8 @@ 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 = ""; }; - F97288E607D277570031794D /* SectCreate.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = SectCreate.cpp; path = src/SectCreate.cpp; sourceTree = ""; }; - F972890007D27FD00031794D /* SectCreate.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SectCreate.h; path = src/SectCreate.h; 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 = ""; }; 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 = ""; }; @@ -209,13 +210,13 @@ F933E3CB092E84250083EAC8 /* MachOReaderArchive.hpp */, F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, + 3DA587190ACC53BE0015C432 /* LLVMReader.hpp */, F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, F9023C4106D5A254001BBF46 /* ObjectFile.h */, + F98D26850AA779BD00416316 /* OpaqueSection.hpp */, F9023C3F06D5A254001BBF46 /* ld.cpp */, F9C0D48A06DD1E1B001C7193 /* Options.cpp */, F9C0D48B06DD1E1B001C7193 /* Options.h */, - F97288E607D277570031794D /* SectCreate.cpp */, - F972890007D27FD00031794D /* SectCreate.h */, F9EA7582097882F3008B4F1D /* debugline.c */, F9EA7583097882F3008B4F1D /* debugline.h */, F9EA72D4097454FF008B4F1D /* machochecker.cpp */, @@ -231,7 +232,7 @@ F9023C3A06D5A23E001BBF46 /* Products */ = { isa = PBXGroup; children = ( - F9023C3906D5A23E001BBF46 /* ld64 */, + F9023C3906D5A23E001BBF46 /* ld */, F971EED306D5ACF60041D381 /* ObjectDump */, F9EA72CB097454A6008B4F1D /* machocheck */, F9EC77EE0A2F85F6002A3E39 /* rebase */, @@ -249,6 +250,7 @@ F9023C3606D5A23E001BBF46 /* Sources */, F9023C3706D5A23E001BBF46 /* Frameworks */, F97F5025070D0B6300B9FCD7 /* CopyFiles */, + F9FCC3EF0A54A4ED00CEB866 /* ShellScript */, ); buildRules = ( F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */, @@ -258,7 +260,7 @@ ); name = ld; productName = ld64; - productReference = F9023C3906D5A23E001BBF46 /* ld64 */; + productReference = F9023C3906D5A23E001BBF46 /* ld */; productType = "com.apple.product-type.tool"; }; F971EED206D5ACF60041D381 /* ObjectDump */ = { @@ -346,7 +348,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\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# 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 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "cd ${DSTROOT}/usr/bin\nln -s ld ld64"; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -357,7 +374,6 @@ files = ( F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, - F97288E707D277570031794D /* SectCreate.cpp in Sources */, F9EA7584097882F3008B4F1D /* debugline.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -458,10 +474,11 @@ GCC_WARN_UNUSED_VALUE = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = 10.5; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ""; PREBINDING = NO; - PRODUCT_NAME = ld64; + PRODUCT_NAME = ld; SECTORDER_FLAGS = ""; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = "-Wall"; @@ -508,7 +525,7 @@ OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ""; PREBINDING = NO; - PRODUCT_NAME = ld64; + PRODUCT_NAME = ld; SECTORDER_FLAGS = ""; VALID_ARCHS = "i386 ppc"; VERSIONING_SYSTEM = "apple-generic"; diff --git a/src/Architectures.hpp b/src/Architectures.hpp index e735f9e..7089366 100644 --- a/src/Architectures.hpp +++ b/src/Architectures.hpp @@ -35,28 +35,31 @@ struct ppc { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff32, kPointerDiff64, + enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, kBranch24, kBranch24WeakImport, kBranch14, kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow }; + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct ppc64 { typedef Pointer64 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff32, kPointerDiff64, + enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff16, kPointerDiff32, kPointerDiff64, kBranch24, kBranch24WeakImport, kBranch14, kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow }; + kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct x86 { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, - kPCRel32, kPCRel32WeakImport, kAbsolute32 }; + enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff16, + kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct x86_64 @@ -67,7 +70,8 @@ struct x86_64 kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, kBranchPCRel32, kBranchPCRel32WeakImport, kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, - kPCRel32GOT, kPCRel32GOTWeakImport }; + kPCRel32GOT, kPCRel32GOTWeakImport, + kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; diff --git a/src/ExecutableFile.h b/src/ExecutableFile.h index 576554c..8fa7099 100644 --- a/src/ExecutableFile.h +++ b/src/ExecutableFile.h @@ -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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -38,8 +38,6 @@ namespace ExecutableFile { { ObjectFile::Reader* reader; DynamicLibraryOptions options; - bool indirect; // library found indirect. Do not make load command - ObjectFile::Reader* directReader; // direct library which re-exports this library }; class Writer : public ObjectFile::Reader @@ -50,13 +48,16 @@ namespace ExecutableFile { 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, - bool createUUID) = 0; + bool createUUID, bool canScatter, + ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs) = 0; protected: Writer(std::vector&) {}; diff --git a/src/LLVMReader.hpp b/src/LLVMReader.hpp new file mode 100644 index 0000000..e49c5ae --- /dev/null +++ b/src/LLVMReader.hpp @@ -0,0 +1,561 @@ +/* -*- 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@ + */ + +#ifndef __LLVM_READER_H__ +#define __LLVM_READER_H__ + +#include +#include +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ObjectFile.h" +#include "llvm/LinkTimeOptimizer.h" + +#define LLVMLinkTimeOptimizer "LLVMlto.dylib" + +class LLVMReader; + +// +// LLVMReference handles LLVMAtom references. These references facilitate +// symbol resolution. +// + +class LLVMReference : public ObjectFile::Reference +{ +public: + LLVMReference (const char *n) : fName(n), fAtom(0), fFromAtom(0) { } + + bool isTargetUnbound() const { return fAtom == 0; } + bool isFromTargetUnbound() const { return true; } + uint8_t getKind() const { return 0; } + uint64_t getFixUpOffset() const { return 0; } + const char * getTargetName() const { return fName; } + ObjectFile::Atom& getTarget() const { return *fAtom; } + uint64_t getTargetOffset() const { return 0; } + bool hasFromTarget() const { return false; } + ObjectFile::Atom& getFromTarget() const { return *fFromAtom; } + 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) + { fAtom = &a; } + void setFromTarget(ObjectFile::Atom &a) { } + const char * getDescription() const { return NULL; } + +private: + const char * fName; + ObjectFile::Atom * fAtom; + ObjectFile::Atom * fFromAtom; +}; + +ObjectFile::Reference::TargetBinding LLVMReference::getTargetBinding() const +{ + if (strncmp (fName, "__ld64.llvm", 11) == 0) + return kDontBind; + else return kUnboundByName; +} + +// +// LLVMAtom acts as a proxy Atom for the symbols that are exported by LLVM bytecode file. Initially, +// LLVMReader creates LLVMAtoms 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. LLVMAtom holds real Atom and forwards appropriate +// methods to real atom. +// + +class LLVMAtom : public ObjectFile::Atom +{ +public: + ObjectFile::Reader * getFile() const { return fOwner; } + bool getTranslationUnitSource (const char **dir, const char **name) const + { return fRealAtom->getTranslationUnitSource (dir, name); } + const char * getName () const { return fAtomName; } + const char * getDisplayName() const { return this->getName(); } + Scope getScope() const { return fScope; } + DefinitionKind getDefinitionKind() const; + SymbolTableInclusion getSymbolTableInclusion() const + { return fRealAtom->getSymbolTableInclusion(); } + bool dontDeadStrip() const { return false; } + bool isZeroFill() const { return fRealAtom->isZeroFill(); } + uint64_t getSize() const { return fRealAtom->getSize(); } + 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->getSegment(); } + uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); } + ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); } + std::vector* getLineInfo() const { return fRealAtom->getLineInfo(); } + ObjectFile::Alignment getAlignment() const; + void copyRawContent(uint8_t buffer[]) const + { fRealAtom->copyRawContent(buffer); } + void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); } + + LLVMAtom(ObjectFile::Reader *owner, const char *n, llvm::LLVMSymbol *ls); + + void setRealAtom (ObjectFile::Atom *atom) + { fRealAtom = atom; } + 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: + ObjectFile::Reader * fOwner; + const char * fAtomName; + llvm::LLVMSymbol * fLLVMSymbol; + ObjectFile::Atom * fRealAtom; + std::vector fReferences; + ObjectFile::Atom::Scope fScope; + ObjectFile::Atom::DefinitionKind fDefKind; +}; + +ObjectFile::Atom::DefinitionKind LLVMAtom::getDefinitionKind() const +{ + if (fRealAtom) + return fRealAtom->getDefinitionKind(); + else + return fDefKind; +} + +LLVMAtom::LLVMAtom(ObjectFile::Reader *owner, const char *n, llvm::LLVMSymbol *ls) : fOwner(owner), fAtomName(n), fLLVMSymbol(ls), fRealAtom(0) +{ + + if (!ls) return; + + switch (ls->getLinkage()) { + case llvm::LTOExternalLinkage: + fScope = scopeGlobal; + fDefKind = kRegularDefinition; + break; + case llvm::LTOLinkOnceLinkage: + case llvm::LTOWeakLinkage: + // ??? How to differentiate between this two linkage types ? + fScope = scopeGlobal; + fDefKind = kWeakDefinition; + break; + default: + throw "Unexpected LLVM Symbol Linkage info\n"; + break; + } +} + +ObjectFile::Alignment LLVMAtom::getAlignment() const +{ + if (fRealAtom) + return fRealAtom->getAlignment(); + else { + ObjectFile::Alignment alignment(fLLVMSymbol->getAlignment()); + return alignment; + } +} + +// +// LLVMReader does not expose internal symbols defined and used inside bytecode file. However, +// these symbols may refere other external symbols. IntercessorAtom facilitate by acting as a +// orignator of such references during pre-optimization symbol resoultion phase. These atoms +// are immediately removed after optimization. +// + +class IntercessorAtom : public ObjectFile::Atom +{ +public: + ObjectFile::Reader * getFile() const { return fOwner; } + bool getTranslationUnitSource (const char **dir, const char **name) const + { return false; } + const char * getName () const { return fAtomName; } + const char * getDisplayName() const { return this->getName(); } + Scope getScope() const { return scopeGlobal; } + DefinitionKind getDefinitionKind() const { return kRegularDefinition; } + SymbolTableInclusion getSymbolTableInclusion() const + { return kSymbolTableNotIn; } + bool dontDeadStrip() const { return false; } + bool isZeroFill() 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 this->getSegment(); } + uint32_t getOrdinal() const { return 0; } + ObjectFile::Atom& getFollowOnAtom() const { return this->getFollowOnAtom(); } + std::vector* getLineInfo() const { return NULL; } + ObjectFile::Alignment getAlignment() const { ObjectFile::Alignment a(0); return a; } + void copyRawContent(uint8_t buffer[]) const + { } + void setScope(Scope s) { } + + + IntercessorAtom(ObjectFile::Reader *owner, std::set &references); + + void addReference(ObjectFile::Reference *ref) + { fReferences.push_back(ref); } + void addReferences(std::set &references); +private: + ObjectFile::Reader * fOwner; + char * fAtomName; + std::vector fReferences; + ObjectFile::Atom::Scope fScope; + ObjectFile::Atom::DefinitionKind fDefKind; +}; + +IntercessorAtom::IntercessorAtom(ObjectFile::Reader *owner, std::set &references) +{ + static int sCount = 0; + fOwner = owner; + fAtomName = (char *) malloc (sizeof(char)*20); + sprintf (fAtomName,"__ld64.llvm%d__",sCount++); + + for (std::set::iterator it = references.begin(); it != references.end(); it++) { + std::string r = *it; + this->addReference(new LLVMReference(r.c_str())); + } +} + +void IntercessorAtom::addReferences(std::set &references) +{ + for (std::set::iterator it = references.begin(); it != references.end(); it++) { + std::string r = *it; + this->addReference(new LLVMReference(r.c_str())); + } +} + +class InIntercessorSet +{ +public: + InIntercessorSet(std::set& iAtoms) : fIntercessorAtoms(iAtoms) {} + + bool operator()(ObjectFile::Atom*& atom) const { + return ( fIntercessorAtoms.count(atom) != 0 ); + } + +private: + std::set& fIntercessorAtoms; +}; + +// +// LLVMOptimizer class is responsible for communicating with LLVM LTO library. +// One LLVMOptimizer object is created per Linker invocation. All LLVMReaders share this +// one single optimizer object. +// + +class LLVMOptimizer +{ +public: + LLVMOptimizer(Options &opt); + ~LLVMOptimizer() { if (fLLVMHandle) dlclose(fLLVMHandle); } + + + void optimize(std::vector&, std::vector&, uint32_t); + void read(ObjectFile::Reader *, const char *, std::set&, std::vector&, const char *); + void reconcileOptimizedAtoms(std::vector&, std::vector&); + void addIntercessor(IntercessorAtom * atom) { fIntercessorAtoms.insert(atom); } + void addReader(ObjectFile::Reader *reader) { fLLVMReaders[reader->getPath()] = reader; } + cpu_type_t getCpuType(std::string &targetTriple); + bool validArchitecture(const char *path, cpu_type_t architecture); + class LCStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef hash_map, LCStringEquals> LLVMAtomToNameMapper; + typedef hash_map, LCStringEquals> ReaderToPathMapper; + + typedef llvm::LinkTimeOptimizer * (*createLLVMOptimizer_func_t) (); +private: + bool fOptimized; + llvm::LinkTimeOptimizer *fOptimizer; + void *fLLVMHandle; + LLVMAtomToNameMapper fLLVMSymbols; + Options& fOptions; + std::set fIntercessorAtoms; + ReaderToPathMapper fLLVMReaders; +}; + +LLVMOptimizer::LLVMOptimizer(Options &opts) : fOptions(opts) +{ + fLLVMHandle = (llvm::LinkTimeOptimizer *) dlopen (LLVMLinkTimeOptimizer, RTLD_LAZY); + if (!fLLVMHandle) + throwf("Unable to load LLVM library: \n", dlerror()); + + createLLVMOptimizer_func_t createLLVMOptimizer_fp = (createLLVMOptimizer_func_t)dlsym(fLLVMHandle, "createLLVMOptimizer"); + if (createLLVMOptimizer_fp == NULL) + throwf("couldn't find \"createLLVMOptimizer\" ", dlerror()); + fOptimizer = createLLVMOptimizer_fp(); + fOptimized = false; +} + +cpu_type_t LLVMOptimizer::getCpuType(std::string &targetTriple) +{ + if ( strncmp (targetTriple.c_str(), "powerpc-", 8) == 0) + return CPU_TYPE_POWERPC; + else if ( strncmp (targetTriple.c_str(), "powerpc64-", 10)) + return CPU_TYPE_POWERPC64; + // match "i[3-9]86-*". + else if ( targetTriple.size() >= 5 && targetTriple[0] == 'i' && targetTriple[2] == '8' && targetTriple[3] == '6' && targetTriple[4] == '-' && targetTriple[1] - '3' < 6 ) + return CPU_TYPE_I386; + else + return CPU_TYPE_ANY; +} + +bool LLVMOptimizer::validArchitecture(const char *path, cpu_type_t architecture) +{ + std::string targetTriple; + fOptimizer->getTargetTriple(path, targetTriple); + if (architecture != getCpuType(targetTriple)) { + fOptimizer->removeModule(path); + return false; + } + + return true; +} + +void LLVMOptimizer::optimize(std::vector &allAtoms, std::vector &newAtoms, uint32_t nextInputOrdinal) +{ + if (fOptimized) + return; + + char * tmp = "/tmp/ld64XXXXXXXX"; + char * bigOfile = (char *) malloc (strlen (tmp) + 3); + if (!bigOfile) + throw "Unable to create temp file name"; + strcpy (bigOfile, tmp); + mktemp (bigOfile); + strcat (bigOfile, ".o"); + + std::vector exportList; + for (std::vector::iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + ObjectFile::Atom *atom = *it; + if (atom->getName()) { + ReaderToPathMapper::iterator pos = fLLVMReaders.find(atom->getFile()->getPath()); + if (pos != fLLVMReaders.end()) + exportList.push_back(atom->getName()); + } + + } + + std::string targetTriple; + llvm::LTOStatus status = fOptimizer->optimizeModules(bigOfile, exportList, targetTriple, fOptions.saveTempFiles(), fOptions.getOutputFilePath()); + if (status != llvm::LTO_OPT_SUCCESS) { + if (status == llvm::LTO_WRITE_FAILURE) + throw "Unable to write optimized output file"; + if (status == llvm::LTO_ASM_FAILURE) + throw "Unable to assemble optimized output file"; + if (status == llvm::LTO_MODULE_MERGE_FAILURE) + throw "Unable to merge bytecode files"; + if (status == llvm::LTO_NO_TARGET) + throw "Unable to load target optimizer"; + } + fOptimized = true; + + Options::FileInfo info = fOptions.findFile (bigOfile); + ObjectFile::Reader* nr = NULL; + 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); + + cpu_type_t cpt = getCpuType(targetTriple); + switch (cpt) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Reader::validFile(p) ) + nr = new mach_o::relocatable::Reader(p, info.path, info.modTime, fOptions.readerOptions(), nextInputOrdinal); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Reader::validFile(p) ) + nr = new mach_o::relocatable::Reader(p, info.path, info.modTime, fOptions.readerOptions(), nextInputOrdinal); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Reader::validFile(p) ) + nr = new mach_o::relocatable::Reader(p, info.path, info.modTime, fOptions.readerOptions(), nextInputOrdinal); + break; + default: + throw "file is not of required architecture"; + break; + } + + std::vector optimizedAtoms; + optimizedAtoms = nr->getAtoms(); + reconcileOptimizedAtoms(optimizedAtoms, newAtoms); + + allAtoms.erase(std::remove_if(allAtoms.begin(), allAtoms.end(), InIntercessorSet(fIntercessorAtoms)), allAtoms.end()); + unlink(bigOfile); + free(bigOfile); +} + +void LLVMOptimizer::read(ObjectFile::Reader *reader, const char *path, std::set &references, std::vector &atoms, const char *intercessorName) +{ + llvm::LinkTimeOptimizer::NameToSymbolMap symbols; + llvm::LTOStatus status = fOptimizer->readLLVMObjectFile (path, symbols, references); + if (status != llvm::LTO_READ_SUCCESS) + throw "Unable to read LLVM bytecode file"; + + for (llvm::LinkTimeOptimizer::NameToSymbolMap::iterator itr = symbols.begin(); + itr != symbols.end(); itr++) { + const char *name = itr->first; + llvm::LLVMSymbol *ls = itr->second; + LLVMAtom *a = new LLVMAtom(reader, name, ls); + + LLVMAtomToNameMapper::iterator pos = fLLVMSymbols.find(name); + bool insertNewAtom = true; + if (pos != fLLVMSymbols.end()) { + LLVMAtom *existingAtom = pos->second; + ObjectFile::Atom::DefinitionKind newDefKind = a->getDefinitionKind(); + ObjectFile::Atom::DefinitionKind existingDefKind = existingAtom->getDefinitionKind(); + if (newDefKind == ObjectFile::Atom::kRegularDefinition + && existingDefKind == ObjectFile::Atom::kRegularDefinition) + throwf ("duplicate symbol %s in %s and %s\n", name, a->getFile()->getPath(), existingAtom->getFile()->getPath()); + else if (newDefKind == ObjectFile::Atom::kWeakDefinition + && existingDefKind == ObjectFile::Atom::kRegularDefinition) + insertNewAtom = false; + else if (newDefKind == ObjectFile::Atom::kWeakDefinition + && existingDefKind == ObjectFile::Atom::kWeakDefinition) + // pick one + insertNewAtom = false; + else if (newDefKind == ObjectFile::Atom::kRegularDefinition + && existingDefKind == ObjectFile::Atom::kWeakDefinition) + insertNewAtom = true; + } + if (insertNewAtom) { + atoms.push_back(a); + fLLVMSymbols[name] = a; + a->addReference(new LLVMReference (intercessorName)); + } + } +} + +void LLVMOptimizer::reconcileOptimizedAtoms(std::vector& optimizedAtoms, + std::vector& newAtoms) +{ + for (std::vector::iterator itr = optimizedAtoms.begin(); + itr != optimizedAtoms.end(); ++itr) { + + ObjectFile::Atom* atom = *itr; + if (!atom->getName()) { + newAtoms.push_back(atom); + continue; + } + + LLVMAtomToNameMapper::iterator pos = fLLVMSymbols.find(atom->getName()); + if ( pos != fLLVMSymbols.end() ) { + + LLVMAtom *la = fLLVMSymbols[atom->getName()]; + la->setRealAtom(atom); + + } + else + newAtoms.push_back(atom); + } +} + +// +// LLVM bytecode file reader +// + +class LLVMReader : public ObjectFile::Reader +{ +public: + static bool validFile(const uint8_t* fileContent, const char *path, cpu_type_t architecture, Options &opts); + static LLVMReader* make(const uint8_t* fileContent, const char* path, time_t modTime, Options& options) + { return new LLVMReader(fileContent, path, modTime, options); } + virtual ~LLVMReader(); + 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; } + + ObjectFile::Atom * retriveIntercessorAtom() { fAtoms.pop_back();return fIntercessorAtom; } + ObjectFile::Atom * getIntercessorAtom() { return fIntercessorAtom; } + +private: + + LLVMReader(const uint8_t* fileContent, const char* path, time_t modTime, Options& options); + void optimize(std::vector& allAtoms, std::vector &newAtoms, uint32_t); + + const char* fPath; + time_t fModTime; + std::vector fAtoms; + IntercessorAtom * fIntercessorAtom; + static LLVMOptimizer *fOptimizer; + std::set fLLVMReferences; +}; + +LLVMOptimizer *LLVMReader::fOptimizer = NULL; + +LLVMReader::~LLVMReader() +{ + if (fOptimizer) + delete fOptimizer; +} + +LLVMReader::LLVMReader (const uint8_t* fileContent, const char *path, time_t modTime, Options& options) +{ + + fPath = path; + fModTime = modTime; + fIntercessorAtom = new IntercessorAtom(this, fLLVMReferences); + fOptimizer->read(this, path, fLLVMReferences, fAtoms, fIntercessorAtom->getName()); + fIntercessorAtom->addReferences(fLLVMReferences); + fAtoms.push_back(fIntercessorAtom); + fOptimizer->addIntercessor(fIntercessorAtom); + fOptimizer->addReader(this); +} + +bool LLVMReader::validFile(const uint8_t* fileContent, const char *path, cpu_type_t architecture, Options &opts) +{ + if (fileContent[0] == 'l' + && fileContent[1] == 'l' + && fileContent[2] == 'v' + && (fileContent[3] == 'c' || fileContent[3] == 'm')) { + + // create optimizer + if (!fOptimizer) + fOptimizer = new LLVMOptimizer(opts); + + if (fOptimizer->validArchitecture(path, architecture)) + return true; + } + + return false; +} + +void LLVMReader::optimize(std::vector &allAtoms, std::vector &newAtoms, uint32_t nextInputOrdinal) +{ + if (fOptimizer) + fOptimizer->optimize(allAtoms, newAtoms, nextInputOrdinal); +} + +#endif + diff --git a/src/MachOFileAbstraction.hpp b/src/MachOFileAbstraction.hpp index 5d46cbf..e0f5620 100644 --- a/src/MachOFileAbstraction.hpp +++ b/src/MachOFileAbstraction.hpp @@ -548,6 +548,106 @@ private: }; + + +// +// 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 // @@ -603,6 +703,55 @@ private: }; +// +// 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: + 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: + rpath_command fields; +}; + // @@ -692,13 +841,16 @@ public: 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) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + 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; diff --git a/src/MachOReaderArchive.hpp b/src/MachOReaderArchive.hpp index c5c120b..c01a603 100644 --- a/src/MachOReaderArchive.hpp +++ b/src/MachOReaderArchive.hpp @@ -52,13 +52,13 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent, uint64_t fileLength); - static Reader* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, const ObjectFile::ReaderOptions& options) - { return new Reader(fileContent, fileLength, path, modTime, options); } + 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 0; } + virtual time_t getModificationTime(){ return fModTime; } virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } virtual std::vector& getAtoms(); virtual std::vector* getJustInTimeAtomsFor(const char* name); @@ -89,10 +89,6 @@ private: typedef typename A::P P; typedef typename A::P::E E; - Reader(const uint8_t fileContent[], uint64_t fileLength, - const char* path, time_t modTime, const ObjectFile::ReaderOptions& options); - const struct ranlib* ranlibBinarySearch(const char* name); - const struct ranlib* ranlibLinearSearch(const char* name); const struct ranlib* ranlibHashSearch(const char* name); ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); void dumpTableOfContents(); @@ -101,6 +97,7 @@ private: const char* fPath; time_t fModTime; const ObjectFile::ReaderOptions& fOptions; + uint32_t fOrdinalBase; const uint8_t* fFileContent; uint64_t fFileLength; const struct ranlib* fTableOfContents; @@ -157,10 +154,10 @@ template time_t Reader::Entry::getModTime() const { char temp[14]; - strncpy(temp, this->ar_size, 12); + strncpy(temp, this->ar_date, 12); temp[12] = '\0'; char* endptr; - return (time_t)strtol(temp, &endptr, 12); + return (time_t)strtol(temp, &endptr, 10); } @@ -220,9 +217,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 ObjectFile::ReaderOptions& options) - : fPath(NULL), fModTime(modTime), fOptions(options), fFileContent(NULL), fTableOfContents(NULL), fTableOfContentCount(0), - fStringPool(NULL) +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; @@ -231,6 +229,10 @@ Reader::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* 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) ) { @@ -261,21 +263,16 @@ ObjectFile::Reader* Reader::makeObjectReaderForMember(const Entry* member) strcat(memberPath, ")"); //fprintf(stderr, "using %s from %s\n", memberName, fPath); try { - ObjectFile::Reader* obj = mach_o::relocatable::Reader::make(member->getContent(), memberPath, fModTime, fOptions); - unsigned int objIndex = 0; - for (class std::set::iterator it=fPossibleEntries.begin(); it != fPossibleEntries.end(); it++, objIndex++) { - if ( *it == member ) - break; - } - obj->setSortOrder((fSortOrder<<16) + objIndex); - //fprintf(stderr, "%s order = 0x%08X, index=%u\n", memberPath, obj->getSortOrder(), objIndex); - return obj; + // 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; + return new typename mach_o::relocatable::Reader::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase); } catch (const char* msg) { throwf("in %s, %s", memberPath, msg); } } + template std::vector& Reader::getAtoms() { @@ -283,18 +280,36 @@ std::vector& Reader::getAtoms() // 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]; - unsigned int objIndex = 0; 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); - r->setSortOrder((fSortOrder<<16) + objIndex++); std::vector& atoms = r->getAtoms(); fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); } 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()); + } + } + } + return fAllAtoms; + } else { // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed return fgEmptyList; @@ -302,42 +317,6 @@ std::vector& Reader::getAtoms() } -template -ConstRanLibPtr Reader::ranlibBinarySearch(const char* key) -{ - const struct ranlib* base = fTableOfContents; - for (uint32_t n = fTableOfContentCount; n > 0; n /= 2) { - const struct ranlib* pivot = &base[n/2]; - const char* pivotStr = &fStringPool[E::get32(pivot->ran_un.ran_strx)]; - int cmp = strcmp(key, pivotStr); - if ( cmp == 0 ) - return pivot; - if ( cmp > 0 ) { - // key > pivot - // move base to symbol after pivot - base = &pivot[1]; - --n; - } - else { - // key < pivot - // keep same base - } - } - return NULL; -} - -template -ConstRanLibPtr Reader::ranlibLinearSearch(const char* key) -{ - for (uint32_t i = 0; i < fTableOfContentCount; ++i) { - const struct ranlib* entry = &fTableOfContents[i]; - const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; - if ( strcmp(key, entryName) == 0 ) - return entry; - } - return NULL; -} - template ConstRanLibPtr Reader::ranlibHashSearch(const char* name) { diff --git a/src/MachOReaderDylib.hpp b/src/MachOReaderDylib.hpp index 055dd4f..28d019c 100644 --- a/src/MachOReaderDylib.hpp +++ b/src/MachOReaderDylib.hpp @@ -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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -72,7 +72,7 @@ private: // // An ExportAtom has no content. It exists so that the linker can track which imported -// symbols can from which dynamic libraries. +// symbols came from which dynamic libraries. // template class ExportAtom : public ObjectFile::Atom @@ -92,8 +92,8 @@ public: virtual bool mustRemainInSection() const { return false; } virtual const char* getSectionName() const { return "._imports"; } virtual Segment& getSegment() const { return fgImportSegment; } - virtual bool requiresFollowOnAtom() const{ return false; } 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 {} @@ -104,12 +104,13 @@ protected: friend class Reader; typedef typename A::P P; - ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak) - : fOwner(owner), fName(name), fWeakDefinition(weak) {} + 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; @@ -123,6 +124,92 @@ 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 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 @@ -133,9 +220,9 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent, bool executableOrDylib); - static Reader* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, - bool executableOrDylib, const ObjectFile::ReaderOptions& options) - { return new Reader(fileContent, fileLength, path, executableOrDylib, options); } + Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, + bool executableOrDylib, const ObjectFile::ReaderOptions& options, + uint32_t ordinalBase); virtual ~Reader() {} virtual const char* getPath() { return fPath; } @@ -144,16 +231,26 @@ public: 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 std::vector* getDependentLibraryPaths(); - virtual bool reExports(ObjectFile::Reader*); + 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 void setImplicitlyLinked() { fImplicitlyLinked = true; } + protected: - const char* parentUmbrella() { return fParentUmbrella; } + + struct ReExportChain { ReExportChain* prev; Reader* reader; }; + + void assertNoReExportCycles(std::set& chainedReExportReaders, ReExportChain*); private: typedef typename A::P P; @@ -164,14 +261,15 @@ private: public: bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; }; + 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; }; - Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - bool executableOrDylib, const ObjectFile::ReaderOptions& options); + bool isPublicLocation(const char* path); + void addSymbol(const char* name, bool weak, uint32_t ordinal); const char* fPath; const char* fParentUmbrella; @@ -180,8 +278,21 @@ private: uint32_t fDylibTimeStamp; uint32_t fDylibtCurrentVersion; uint32_t fDylibCompatibilityVersion; + uint32_t fReExportedOrdinal; std::vector fDependentLibraryPaths; NameToAtomMap fAtoms; + NameSet fIgnoreExports; + bool fNoRexports; + const bool fLinkingFlat; + const bool fLinkingMainExecutable; + bool fExplictReExportFound; + bool fExplicitlyLinked; + bool fImplicitlyLinked; + bool fProvidedAtom; + ObjectFile::Reader::ObjcConstraint fObjcContraint; + std::vector fReExportedChildren; + const ObjectFile::ReaderOptions::VersionMin fDeploymentVersionMin; + std::vector fFlatImports; static bool fgLogHashtable; static std::vector fgEmptyAtomList; @@ -194,8 +305,13 @@ bool Reader::fgLogHashtable = false; template -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, bool executableOrDylib, const ObjectFile::ReaderOptions& options) - : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), fDylibCompatibilityVersion(0) +Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, bool executableOrDylib, + 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), fObjcContraint(ObjectFile::Reader::kObjcNone), + fDeploymentVersionMin(options.fVersionMin) { // sanity check if ( ! validFile(fileContent, executableOrDylib) ) @@ -207,6 +323,16 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p const uint32_t cmd_count = header->ncmds(); const macho_load_command

* const cmds = (macho_load_command

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

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

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - PathAndFlag entry; - entry.path = strdup(((struct macho_dylib_command

*)cmd)->name()); - entry.reExport = false; - fDependentLibraryPaths.push_back(entry); - break; + 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()); } - cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); } - + // pass 2 determines re-export info const macho_dysymtab_command

* dynamicInfo = NULL; const macho_nlist

* symbolTable = NULL; @@ -247,14 +383,16 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p 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 ( !options.fFlatNamespace ) { + 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; @@ -265,7 +403,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p } break; case LC_SUB_LIBRARY: - if ( !options.fFlatNamespace ) { + 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; @@ -285,21 +423,51 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p 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 ) { + fprintf(stderr, "ld: warning, can't parse __OBJC/__image_info section in %s\n", fPath); + } + } + } + } } cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); } + // 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()); } @@ -311,48 +479,127 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p if ( dynamicInfo == NULL ) throw "dylib 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, "ld64: 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(), path); 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) { - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weak = ((sym->n_desc() & N_WEAK_DEF) != 0); - const char* name = strdup(&strings[sym->n_strx()]); - if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); - fAtoms[name] = bucket; + 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, "ld64: building hashtable of %u entries for %s\n", count, path); + 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]; - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weak = ((sym->n_desc() & N_WEAK_DEF) != 0); - const char* name = strdup(&strings[sym->n_strx()]); - if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); - fAtoms[name] = bucket; + 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; + } + 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 { + fprintf(stderr, "ld: warning bad symbol action: %s in dylib %s\n", name, this->getPath()); + } + } + } + else { + fprintf(stderr, "ld: warning bad symbol name: %s in dylib %s\n", name, this->getPath()); + } + } + else { + fprintf(stderr, "ld: warning bad symbol version: %s in dylib %s\n", name, this->getPath()); + } + } + else { + fprintf(stderr, "ld: warning bad symbol condition: %s in dylib %s\n", 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() { - // TO DO: for flat-namespace libraries, when linking flat_namespace - // we need to create an atom which references all undefines - return fgEmptyAtomList; + return fFlatImports; } @@ -360,12 +607,13 @@ 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.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 @@ -374,6 +622,25 @@ 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; + } + } + } } return atoms; } @@ -381,48 +648,119 @@ std::vector* Reader::getJustInTimeAtomsFor(const cha template -std::vector* Reader::getDependentLibraryPaths() +bool Reader::isPublicLocation(const char* path) { - std::vector* result = new std::vector; - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - result->push_back(it->path); + // /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 result; + + return false; } template -std::vector* Reader::getAllowableClients() +void Reader::processIndirectLibraries(DylibHander* handler) { - std::vector* result = new std::vector; - for (typename std::vector::iterator it = fAllowableClients.begin(); - it != fAllowableClients.end(); - it++) { - result->push_back(*it); + if ( fLinkingFlat ) { + for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { + handler->findDylib(it->path, this->getPath()); + } } - return (fAllowableClients.size() != 0 ? result : NULL); + 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 + std::set chainedReExportReaders; + ReExportChain chain; + chain.prev = NULL; + chain.reader = this; + this->assertNoReExportCycles(chainedReExportReaders, &chain); } template -bool Reader::reExports(ObjectFile::Reader* child) +void Reader::assertNoReExportCycles(std::set& chainedReExportReaders, ReExportChain* prev) { - // A dependent dylib is re-exported under two conditions: - // 1) parent contains LC_SUB_UMBRELLA or LC_SUB_LIBRARY with child name - const char* childInstallPath = child->getInstallPath(); - for (typename std::vector::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - if ( it->reExport && ((strcmp(it->path, child->getPath()) == 0) || ((childInstallPath!=NULL) && (strcmp(it->path, childInstallPath)==0))) ) - return true; + // check none of my re-exported dylibs are already in set + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + if ( chainedReExportReaders.count(*it) != 0 ) { + // we may want to print out the chain of dylibs causing the cylce... + throwf("cycle in dylib re-exports with %s", this->getPath()); + } } - - // 2) child contains LC_SUB_FRAMEWORK with parent name - 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) ) - return true; + // recursively check my re-exportted dylibs + chainedReExportReaders.insert(this); + ReExportChain chain; + chain.prev = prev; + chain.reader = this; + for (std::vector::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { + ((Reader*)(*it))->assertNoReExportCycles(chainedReExportReaders, &chain); } +} - return false; + +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 <> diff --git a/src/MachOReaderRelocatable.hpp b/src/MachOReaderRelocatable.hpp index 310b561..7f20ae3 100644 --- a/src/MachOReaderRelocatable.hpp +++ b/src/MachOReaderRelocatable.hpp @@ -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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -78,7 +78,6 @@ public: // forward reference template class Reader; -template class SymbolAtomSorter; struct AtomAndOffset { @@ -104,14 +103,13 @@ public: virtual ~Reference() {} - virtual bool isTargetUnbound() const { return ( fToTarget.atom == NULL ); } - virtual bool isFromTargetUnbound() const { return ( fFromTarget.atom == NULL ); } + 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 bool hasFromTarget() const { return ( (fFromTarget.atom != NULL) || (fFromTargetName != NULL) ); } 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; } @@ -122,6 +120,7 @@ public: virtual const char* getDescription() const; virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } + static bool fgForFinalLinkedImage; private: pint_t fFixUpOffsetInSrc; @@ -130,8 +129,10 @@ private: const char* fToTargetName; const char* fFromTargetName; Kinds fKind; + }; +template bool Reference::fgForFinalLinkedImage = true; template Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget) @@ -141,10 +142,12 @@ Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset // 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) - && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) ) { - //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d\n", toTarget.atom, fToTargetName, toTarget.atom->getScope()); + && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) + && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) ) { 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); @@ -159,6 +162,7 @@ Reference::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset // make reference a by-name where needed if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) + && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) && (toTarget.atom != at.atom) ) { fToTargetName = toTarget.atom->getName(); fToTarget.atom = NULL; @@ -177,6 +181,39 @@ Reference::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, ((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 @@ -221,6 +258,18 @@ public: 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 { @@ -231,9 +280,76 @@ public: 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() ) + fprintf(stderr, "ld: atom sorting error for %s and %s in %s\n", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); + break; + } + } + } + return false; + } }; @@ -255,23 +371,24 @@ public: 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 { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } + virtual bool dontDeadStrip() const; virtual bool isZeroFill() const { return ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL); } 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 bool requiresFollowOnAtom() const; 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) { fSize = size; } + 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; @@ -282,7 +399,6 @@ protected: typedef typename ReferenceVector::iterator ReferenceVectorIterator; // seems to help C++ parser typedef typename ReferenceVector::const_iterator ReferenceVectorConstIterator; // seems to help C++ parser friend class Reader; - friend class SymbolAtomSorter; SymbolAtom(Reader&, const macho_nlist

*, const macho_section

*); virtual ~SymbolAtom() {} @@ -388,17 +504,40 @@ SymbolAtom::SymbolAtom(Reader& owner, const macho_nlist

* symbol, const // 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; } // compute alignment fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); + + // 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); @@ -408,20 +547,6 @@ const char* SymbolAtom::getSectionName() const return fSection->sectname(); } -template -bool SymbolAtom::requiresFollowOnAtom() const -{ - // requires follow-on if built with old compiler and not the last atom - if ( (fOwner.fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0) { - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return true; - } - } - return false; -} - template ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const { @@ -434,7 +559,27 @@ ObjectFile::Atom& SymbolAtom::getFollowOnAtom() const } +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 @@ -448,36 +593,85 @@ void SymbolAtom::copyRawContent(uint8_t buffer[]) const } } +// +// A SymbolAliasAtom represents an alternate name for a SymbolAtom +// +// template -class SymbolAtomSorter +class SymbolAliasAtom : public BaseAtom { public: - SymbolAtomSorter(std::map& map) : fMap(map) {} - - typedef typename A::P::uint_t pint_t; - - bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) - { - pint_t leftAddr = ((SymbolAtom*)left)->fAddress; - pint_t rightAddr = ((SymbolAtom*)right)->fAddress; - if ( leftAddr == rightAddr ) { - // two atoms with same address, must have been a function with multiple labels - // make sure we sort these so the one with real content (in map) is last - std::map::iterator pos = fMap.find(leftAddr); - if ( pos != fMap.end() ) { - return ( pos->second == right ); - } - return false; - } - else { - return ( leftAddr < rightAddr ); - } - } -private: - std::map& fMap; + 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 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 @@ -501,18 +695,19 @@ public: 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 { return "__common"; } + virtual const char* getSectionName() const; virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; } - virtual bool requiresFollowOnAtom() const { return false; } 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 "ld64: can't add references"; } + virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld64: can't add line info to tentative definition"; } + 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; @@ -557,17 +752,34 @@ TentativeAtom::TentativeAtom(Reader& owner, const macho_nlist

* symbol) template ObjectFile::Alignment TentativeAtom::getAlignment() const { - // common symbols align to their size - // that is, a 4-byte common aligns to 4-bytes - // to be safe, odd size commons align to the next power-of-2 size - uint8_t alignment = (uint8_t)ceil(log2(this->getSize())); + 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 < 15 ) + if ( alignment < 12 ) return ObjectFile::Alignment(alignment); else - return ObjectFile::Alignment(15); + 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 { @@ -598,7 +810,6 @@ public: virtual bool mustRemainInSection() const { return true; } virtual const char* getSectionName() const; virtual Segment& getSegment() const { return *fSegment; } - virtual bool requiresFollowOnAtom() const; virtual ObjectFile::Atom& getFollowOnAtom() const; virtual std::vector* getLineInfo() const { return NULL; } virtual ObjectFile::Alignment getAlignment() const; @@ -607,10 +818,13 @@ public: 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) { fprintf(stderr, "ld64: can't add line info to anonymous symbol %s from %s\n", this->getDisplayName(), this->getFile()->getPath()); } + virtual void addLineInfo(const ObjectFile::LineInfo& info) { fprintf(stderr, "ld: can't add line info to anonymous symbol %s from %s\n", 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; @@ -623,9 +837,11 @@ protected: AnonymousAtom(Reader&, const macho_section

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

* fSection; uint32_t fAddress; uint32_t fSize; @@ -640,25 +856,27 @@ protected: template AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* section, uint32_t addr, uint32_t size) - : fOwner(owner), fSynthesizedName(NULL), fSection(section), fAddress(addr), fSize(size), fSegment(NULL), fDontDeadStrip(true), - fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), + : 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) { fSegment = new Segment(fSection); fRedirect = this; uint8_t type = fSection->flags() & SECTION_TYPE; + //fprintf(stderr, "AnonymousAtom(%p) addr=0x%X in %s from %s\n", this, 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 - uint32_t classNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 2*sizeof(pint_t) - section->addr())); - const char* str = (char*)(owner.fHeader) + section->offset() + classNameAddr - section->addr(); - asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", str); + fSynthesizedName = ".objc_class_name_PENDING"; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); if ( fOwner.fOptions.fForFinalLinkedImage ) fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; else @@ -677,6 +895,8 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio asprintf((char**)&fSynthesizedName, "cstring=%s", str); fScope = ObjectFile::Atom::scopeLinkageUnit; fDontDeadStrip = false; + if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() ) + fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; } break; case S_4BYTE_LITERALS: @@ -706,10 +926,13 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio 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); + //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; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); } break; case S_MOD_INIT_FUNC_POINTERS: @@ -731,12 +954,16 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio 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_desc() & N_WEAK_DEF) == 0) ) { + if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) { BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName); - if ( staticAtom != NULL ) + if ( staticAtom != NULL ) fRedirect = staticAtom; } - fScope = ObjectFile::Atom::scopeLinkageUnit; + // 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: @@ -752,23 +979,40 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio 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) - && (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; + && ((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; + } } } - throwf("malformed .o file: non-lazy-pointer at address 0x%08X with value 0x%0llX missing symbol", addr, (uint64_t)nonLazyPtrValue); + // 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()]; @@ -780,6 +1024,14 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio strcat(str, "$non_lazy_ptr"); fSynthesizedName = str; + // optimize __IMPORT segment out of i386 dyld + if ( fOwner.fOptions.fForDyld && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { + macho_section

* dummySection = new macho_section

(*fSection); + dummySection->set_segname("__DATA"); + fSection = dummySection; + fSegment = new Segment(fSection); + } + 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()); @@ -799,6 +1051,47 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio //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) { + ObjectFile::Reference* ref = this->getReferences()[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]); + } + } +} + template const char* AnonymousAtom::getDisplayName() const @@ -806,17 +1099,20 @@ const char* AnonymousAtom::getDisplayName() const if ( fSynthesizedName != NULL ) return fSynthesizedName; - static char temp[512]; + if ( fDisplayName != NULL ) + return fDisplayName; + if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) { uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - sprintf(temp, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); + asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); } else { - sprintf(temp, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); + asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); } - return temp; + return fDisplayName; } + template ObjectFile::Atom::Scope AnonymousAtom::getScope() const { @@ -832,9 +1128,14 @@ ObjectFile::Atom::DefinitionKind AnonymousAtom::getDefinitionKind() const case S_4BYTE_LITERALS: case S_8BYTE_LITERALS: case S_16BYTE_LITERALS: - case S_NON_LAZY_SYMBOL_POINTERS: + case S_SYMBOL_STUBS: case S_LITERAL_POINTERS: return ObjectFile::Atom::kWeakDefinition; + case S_NON_LAZY_SYMBOL_POINTERS: + if ( fScope == ObjectFile::Atom::scopeTranslationUnit ) + return ObjectFile::Atom::kRegularDefinition; + else + return ObjectFile::Atom::kWeakDefinition; default: return ObjectFile::Atom::kRegularDefinition; } @@ -871,24 +1172,14 @@ ObjectFile::Alignment AnonymousAtom::getAlignment() const 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 -bool AnonymousAtom::requiresFollowOnAtom() const -{ - // requires follow-on if built with old compiler and not the last atom - if ( (fOwner.fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0) { - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return true; - } - } - return false; -} template ObjectFile::Atom& AnonymousAtom::getFollowOnAtom() const @@ -914,6 +1205,78 @@ 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/ +// +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 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 @@ -921,9 +1284,8 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent); - static Reader* make(const uint8_t* fileContent, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options) - { return new Reader(fileContent, path, modTime, options); } + 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; } @@ -932,6 +1294,10 @@ public: 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 ObjectFile::Reader::CpuConstraint getCpuConstraint() { return fCpuConstraint; } + virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); } + virtual bool objcReplacementClasses(){ return fReplacementClasses; } bool getTranslationUnitSource(const char** dir, const char** name) const; @@ -944,8 +1310,10 @@ private: typedef typename A::ReferenceKinds Kinds; friend class AnonymousAtom; friend class TentativeAtom; + friend class AbsoluteAtom; friend class SymbolAtom; - Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options); + + 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); Kinds pointerDiffKindForLength_powerpc(uint8_t r_length); @@ -963,11 +1331,14 @@ private: Reference* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section

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

* toSymbol, uint32_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; @@ -975,9 +1346,13 @@ private: uint32_t fSymbolCount; const macho_segment_command

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

* fDwarfDebugInfoSect; @@ -988,21 +1363,35 @@ private: std::map fDwarfIndexToFile; std::vector fStabs; bool fAppleObjc; + bool fHasDTraceProbes; + bool fHaveIndirectSymbols; + bool fReplacementClasses; + ObjectFile::Reader::ObjcConstraint fObjConstraint; + ObjectFile::Reader::CpuConstraint fCpuConstraint; }; template -Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options) - : fPath(strdup(path)), fModTime(modTime), fOptions(options), fHeader((const macho_header

*)fileContent), +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), - fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false) + fDebugInfo(kDebugInfoNone), fHasUUID(false), fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), + fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), + fHaveIndirectSymbols(false), fReplacementClasses(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

* cmd = cmds; @@ -1016,6 +1405,10 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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: @@ -1046,67 +1439,90 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, // add all atoms that have entries in symbol table const macho_section

* sections = (macho_section

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

)); - for (uint32_t i=0; i < fSymbolCount; ++i) { + 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 ) { - // 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 = new SymbolAtom(*this, &sym, section); - std::map::iterator pos = fAddrToAtom.find(sym.n_value()); - if ( pos != fAddrToAtom.end() ) { - // another label to an existing address - // make this one be the real one and followed by the previous - BaseAtom* existingAtom = pos->second; - //fprintf(stderr, "new atom %s has same address as existing atom %s\n", newAtom->getDisplayName(), existingAtom->getDisplayName()); - new Reference(A::kFollowOn, AtomAndOffset(newAtom), AtomAndOffset(existingAtom)); - newAtom->setSize(0); - } - else { - fAddrToAtom[sym.n_value()] = 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: - fprintf(stderr, "ld64 warning: symbol %s found in unsupported section in %s\n", - &fStrings[sym.n_strx()], this->getPath()); + 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; + std::map::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[sym.n_value()] = 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: + fprintf(stderr, "ld: warning symbol %s found in unsupported section in %s\n", + &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) && (strncmp(&fStrings[sym.n_strx()], ".objc_class_name_", 16) == 0) ) { - fAppleObjc = true; + else if ( type == N_ABS ) { + const char* symName = &fStrings[sym.n_strx()]; + if ( strncmp(symName, ".objc_class_name_", 16) == 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; } } } - // sort SymbolAtoms by address - std::sort(fAtoms.begin(), fAtoms.end(), SymbolAtomSorter(fAddrToAtom)); - // add all fixed size anonymous atoms from special sections for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { uint32_t atomSize = 0; @@ -1146,7 +1562,36 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, // 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 { + fprintf(stderr, "can't parse __OBJC/__image_info section in %s\n", fPath); + } + } break; } if ( atomSize != 0 ) { @@ -1168,7 +1613,9 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { uint32_t stringLen; uint32_t stringAddr; - BaseAtom* firstEmptyString = NULL; + BaseAtom* mostAlignedEmptyString = NULL; + uint32_t mostAlignedEmptyStringTrailingZeros = 0; + std::vector > emptyStrings; for(uint32_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { stringAddr = sect->addr() + sectOffset; stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; @@ -1176,13 +1623,15 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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 - // map them all to the first empty string - if ( firstEmptyString == NULL ) { - firstEmptyString = newAtom; - fAtoms.push_back(firstEmptyString); + // 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; } - fAddrToAtom[stringAddr] = firstEmptyString; } else { fAtoms.push_back(newAtom); @@ -1190,14 +1639,30 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } } + // 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 (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 = ((fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS) == 0); + 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 ) { @@ -1223,26 +1688,26 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, BaseAtom* previousAtom = NULL; if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) { BaseAtom* newAtom = new AnonymousAtom(*this, sect, sect->addr(), 0); - fAtoms.push_back(newAtom); 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::map::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - // note: this algorithm depends on the map iterator returning entries in address order - if ( (it->first >= sectionStartAddr) && (it->first < sectionEndAddr) ) { - //fprintf(stderr, " atom %s in section\n", it->second->getDisplayName()); - if ( previousAtom != NULL ) { - previousAtom->setSize(it->first - previousAtomAddr); - // FIX FIX: this setting of followOn atoms does not work when there are multiple - // labels for the same atom - if ( setFollowOnAtom && (it->second != previousAtom) ) - makeReference(A::kFollowOn, previousAtomAddr, it->first); + for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + BaseAtom* atom = (BaseAtom*)(*it); + uint32_t atomAddr = atom->getObjectAddress(); + if ( atom->getSectionRecord() == sect ) { + //fprintf(stderr, "addr=0x%08X, atom=%s\n", 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 = it->first; - previousAtom = it->second; - } + previousAtomAddr = atomAddr; + previousAtom = atom; + } } if ( previousAtom != NULL ) { // set last atom in section @@ -1254,72 +1719,62 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } - // add relocation based references - for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - // ignore dwarf sections. If ld every 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\n", sect->segname(), sect->sectname(), r, msg); + // 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_", 16) == 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(); + } - // check of object file that defines no classes, but uses classes - if ( !fAppleObjc ) { - 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) && (strncmp(&fStrings[sym.n_strx()], ".objc_class_name_", 16) == 0) ) { - fAppleObjc = true; - break; - } - } - } + // 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) { - // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change - if ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { - // gcc sometimes over aligns class structure - uint32_t align = 1 << sect->align(); - uint32_t classSize = ((12 * sizeof(pint_t)) + align-1) & (-align); - for (uint32_t offset = 0; offset < sect->size(); offset += classSize) { - // add by-name reference to super class - pint_t superClassNameAddr = P::getP(*(pint_t*)(((uint8_t*)fHeader) + sect->offset() + offset + sizeof(pint_t))); - if ( superClassNameAddr != 0 ) { - const char* superStr = (char*)(fHeader) + sect->offset() + superClassNameAddr - sect->addr(); - const char* superClassName; - asprintf((char**)&superClassName, ".objc_class_name_%s", superStr); - makeByNameReference(A::kNoFixUp, sect->addr()+offset+sizeof(pint_t), superClassName, 0); - } - } - } - else if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { + if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { - // scan through __cls_refs and add by-name reference for each required class - uint32_t classNameAddr = P::getP(*(pint_t*)(((uint8_t*)fHeader) + sect->offset() + offset)); - const char* classStr = (char*)(fHeader) + sect->offset() + classNameAddr - sect->addr(); - const char* className; - asprintf((char**)&className, ".objc_class_name_%s", classStr); - makeByNameReference(A::kNoFixUp, sect->addr()+offset, className, 0); + 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); + } + } } } } @@ -1332,7 +1787,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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) ) { @@ -1349,6 +1804,48 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } + // 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 (std::map::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()); @@ -1363,7 +1860,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, // if can't parse dwarf, warn and give up fDwarfTranslationUnitFile = NULL; fDwarfTranslationUnitDir = NULL; - fprintf(stderr, "ld64: warning can't parse dwarf compilation unit info in %s\n", this->getPath()); + fprintf(stderr, "ld: warning can't parse dwarf compilation unit info in %s\n", this->getPath()); fDebugInfo = kDebugInfoNone; } } @@ -1372,7 +1869,8 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, // 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) ) { + 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(); @@ -1385,6 +1883,8 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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; @@ -1402,9 +1902,28 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, curAtom = ao.atom; if ( curAtom == NULL ) break; // file has line info but no functions - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); + 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); @@ -1429,7 +1948,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, line_free(lines); } else { - fprintf(stderr, "ld64: warning could not parse dwarf line number info in %s\n", this->getPath()); + fprintf(stderr, "ld: warning could not parse dwarf line number info in %s\n", this->getPath()); } } } @@ -1480,10 +1999,13 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, 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, ':'); @@ -1513,6 +2035,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, useStab = false; } break; + } case N_FUN: // old style stabs without BNSYM state = inFun; @@ -1551,6 +2074,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, break; case N_LCSYM: case N_STSYM: + { BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom; if ( nestedAtom != NULL ) { stab.atom = nestedAtom; @@ -1561,6 +2085,7 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, type, (uint64_t)sym->n_value(), path); } break; + } case N_LBRAC: case N_RBRAC: case N_SLINE: @@ -1620,12 +2145,6 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } - // sort references in each atom - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - atom->sortReferences(); - } - #if 0 // special case precompiled header .o file (which has no content) to have one empty atom if ( fAtoms.size() == 0 ) { @@ -1637,8 +2156,70 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, } } #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: + fCpuConstraint = ObjectFile::Reader::kCpuAny; + break; + case CPU_SUBTYPE_POWERPC_750: + fCpuConstraint = ObjectFile::Reader::kCpuG3; + break; + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + fCpuConstraint = ObjectFile::Reader::kCpuG4; + break; + case CPU_SUBTYPE_POWERPC_970: + fCpuConstraint = ObjectFile::Reader::kCpuG5; + break; + default: + fprintf(stderr, "ld: warning, unknown cpu-sub-type 0x%08X in %s\n", cpusubtype, fPath); + } +} + + +template +void Reader::setCpuConstraint(uint32_t cpusubtype) +{ + // no cpu sub types for this architecture +} + +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) { @@ -1663,7 +2244,7 @@ bool Reader::getTranslationUnitSource(const char** dir, const char** name) co if ( fDebugInfo == kDebugInfoDwarf ) { *dir = fDwarfTranslationUnitDir; *name = fDwarfTranslationUnitFile; - return true; + return (fDwarfTranslationUnitFile != NULL); } return false; } @@ -1679,7 +2260,7 @@ BaseAtom* Reader::findAtomByName(const char* name) } } // try all atoms, because this might have been a tentative definition - for (std::vector::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { + 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) ) { @@ -1747,10 +2328,11 @@ Reference* Reader::makeReferenceToSymbol(Kinds kind, uint32_t at { // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references // instead check scope of target - if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && ((toSymbol->n_type() & N_EXT) == 0) ) + 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), &fStrings[toSymbol->n_strx()], toOffset); + return new Reference(kind, findAtomAndOffset(atAddr), symbolName, toOffset); } @@ -1768,7 +2350,7 @@ Reference* Reader::makeReferenceToEH(const char* ehName, pint_t return makeReference(x86_64::kNoFixUp, funcAddr, ehAtomAddress); } } - fprintf(stderr, "ld64: warning, can't find matching function for eh symbol %s\n", ehName); + fprintf(stderr, "ld: warning, can't find matching function for eh symbol %s\n", ehName); return NULL; } @@ -2197,7 +2779,15 @@ bool Reader::addRelocReference_powerpc(const macho_section* se } if ( reloc->r_extern() ) { offsetInTarget = srcAddr + displacement; - if ( weakImport ) + 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); @@ -2206,7 +2796,16 @@ bool Reader::addRelocReference_powerpc(const macho_section* se 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; - if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + 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 @@ -2246,7 +2845,17 @@ bool Reader::addRelocReference_powerpc(const macho_section* se } else { dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); - makeReference(A::kAbsLow16, srcAddr, dstAddr); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + std::map::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; @@ -2264,7 +2873,17 @@ bool Reader::addRelocReference_powerpc(const macho_section* se } else { dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeReference(A::kAbsLow14, srcAddr, dstAddr); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + std::map::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; @@ -2281,7 +2900,17 @@ bool Reader::addRelocReference_powerpc(const macho_section* se } else { dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - makeReference(A::kAbsHigh16, srcAddr, dstAddr); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + std::map::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; @@ -2299,7 +2928,17 @@ bool Reader::addRelocReference_powerpc(const macho_section* se } else { dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); + if ( reloc->r_symbolnum() == R_ABS ) { + // find absolute symbol that corresponds to pointerValue + std::map::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; @@ -2318,13 +2957,24 @@ bool Reader::addRelocReference_powerpc(const macho_section* se } break; case PPC_RELOC_JBSR: - // this is from -mlong-branch codegen. We ignore the jump island + // 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 ) { printf("PPC_RELOC_JBSR missing following pair\n"); break; } 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: printf("unknown relocation type %d\n", reloc->r_type()); @@ -2468,6 +3118,41 @@ bool Reader::addRelocReference_powerpc(const macho_section* se printf("PPC_RELOC_SECTDIFF missing following pair\n"); break; } + // need to read section content to see if this has an addend, if so adjust addresses + switch ( sreloc->r_length() ) { + case 0: + if ( (dstAddr - nextRelocValue) != *((uint8_t*)fixUpPtr) ) { + if (findAtomAndOffset(dstAddr).atom == findAtomAndOffset(srcAddr).atom ) + dstAddr = *((uint8_t*)fixUpPtr) + nextRelocValue; + else + nextRelocValue = dstAddr - *((uint8_t*)fixUpPtr); + } + break; + case 1: + if ( (dstAddr - nextRelocValue) != BigEndian::get16(*((uint16_t*)fixUpPtr)) ) { + if (findAtomAndOffset(dstAddr).atom == findAtomAndOffset(srcAddr).atom ) + dstAddr = BigEndian::get16(*((uint16_t*)fixUpPtr)) + nextRelocValue; + else + nextRelocValue = dstAddr - BigEndian::get16(*((uint16_t*)fixUpPtr)); + } + break; + case 2: + if ( (dstAddr - nextRelocValue) != BigEndian::get32(*fixUpPtr) ) { + if (findAtomAndOffset(dstAddr).atom == findAtomAndOffset(srcAddr).atom ) + dstAddr = BigEndian::get32(*fixUpPtr) + nextRelocValue; + else + nextRelocValue = dstAddr - BigEndian::get32(*fixUpPtr); + } + break; + case 3: + if ( (dstAddr - nextRelocValue) != BigEndian::get64(*((uint64_t*)fixUpPtr)) ) { + if (findAtomAndOffset(dstAddr).atom == findAtomAndOffset(srcAddr).atom ) + dstAddr = BigEndian::get64(*((uint64_t*)fixUpPtr)) + nextRelocValue; + else + nextRelocValue = dstAddr - BigEndian::get64(*((uint64_t*)fixUpPtr)); + } + break; + } makeReference(pointerDiffKindForLength_powerpc(sreloc->r_length()), srcAddr, nextRelocValue, dstAddr); } break; @@ -2488,6 +3173,8 @@ ppc::ReferenceKinds Reader::pointerDiffKindForLength_powerpc(uint8_t r_leng { if ( r_length == 2 ) return ppc::kPointerDiff32; + else if ( r_length == 1 ) + return ppc::kPointerDiff16; else throw "bad diff relocations r_length for ppc architecture"; } @@ -2516,35 +3203,74 @@ bool Reader::addRelocReference(const macho_section* sect, const mac switch ( reloc->r_type() ) { case GENERIC_RELOC_VANILLA: { - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - x86::ReferenceKinds kind; + x86::ReferenceKinds kind = x86::kPointer; uint32_t pointerValue = E::get32(*fixUpPtr); if ( reloc->r_pcrel() ) { - kind = x86::kPCRel32; - pointerValue += srcAddr + sizeof(uint32_t); + switch( reloc->r_length() ) { + case 0: + case 3: + throw "bad pc-rel vanilla relocation length"; + 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; + } } 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) ) kind = x86::kPointerWeakImport; const char* targetName = &fStrings[targetSymbol->n_strx()]; - makeByNameReference(kind, srcAddr, targetName, pointerValue); + 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; - if ( reloc->r_pcrel() && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) + 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 + else if ( reloc->r_symbolnum() != R_ABS ) makeReference(kind, srcAddr, pointerValue); + else { + // find absolute symbol that corresponds to pointerValue + std::map::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; @@ -2601,10 +3327,40 @@ bool Reader::addRelocReference(const macho_section* sect, const mac printf("GENERIC_RELOC_SECTDIFF missing following pair\n"); break; } - if ( sreloc->r_length() != 2 ) - throw "bad length for GENERIC_RELOC_SECTDIFF"; - betterDstAddr = LittleEndian::get32(*fixUpPtr); - makeReferenceWithToBase(x86::kPointerDiff, srcAddr, nextRelocValue, betterDstAddr+nextRelocValue, dstAddr); + 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: @@ -2642,10 +3398,20 @@ bool Reader::addRelocReference(const macho_section* sect, con 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() ) + if ( reloc->r_extern() ) { makeReferenceToSymbol(x86_64::kPointer, srcAddr, targetSymbol, dstAddr); - else + } + 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: @@ -2711,6 +3477,14 @@ bool Reader::addRelocReference(const macho_section* sect, con 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: @@ -2720,7 +3494,15 @@ bool Reader::addRelocReference(const macho_section* sect, con throw "length != 2 and X86_64_RELOC_BRANCH not supported"; dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); if ( reloc->r_extern() ) { - if ( isWeakImportSymbol(targetSymbol) ) + 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); @@ -2756,6 +3538,7 @@ bool Reader::addRelocReference(const macho_section* sect, con 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 ) @@ -2818,6 +3601,7 @@ bool Reader::addRelocReference(const macho_section* sect, con else ref->setToTargetOffset(dstAddr); break; + } default: fprintf(stderr, "unknown relocation type %d\n", reloc->r_type()); } @@ -2825,6 +3609,34 @@ bool Reader::addRelocReference(const macho_section* sect, con } +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\n", sect->segname(), sect->sectname(), r, msg); + } + } + } + } +} + + template <> const char* Reference::getDescription() const { @@ -2853,15 +3665,41 @@ const char* Reference::getDescription() const 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::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 ) { @@ -2899,6 +3737,16 @@ const char* Reference::getDescription() const 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 @@ -2920,13 +3768,13 @@ const char* Reference::getDescription() const 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 offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + 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 offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + 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 offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); + 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); @@ -2935,10 +3783,22 @@ const char* Reference::getDescription() const 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 to absolute address of ", fFixUpOffsetInSrc); + 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 to absolute address of ", fFixUpOffsetInSrc); + 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 @@ -2996,6 +3856,16 @@ const char* Reference::getDescription() const 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; @@ -3019,10 +3889,22 @@ const char* Reference::getDescription() const 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 to absolute address of ", fFixUpOffsetInSrc); + 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 to absolute address of ", fFixUpOffsetInSrc); + 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 @@ -3104,6 +3986,18 @@ 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::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 ) { diff --git a/src/MachOWriterExecutable.hpp b/src/MachOWriterExecutable.hpp index 7ac12fe..5b05210 100644 --- a/src/MachOWriterExecutable.hpp +++ b/src/MachOWriterExecutable.hpp @@ -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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,13 @@ #include "MachOFileAbstraction.hpp" +#ifndef S_DTRACE_DOF + #define S_DTRACE_DOF 0xF +#endif + +#ifndef MH_PIE + #define MH_PIE 0x200000 +#endif // // @@ -60,7 +68,7 @@ // Writer::addObjectRelocs() // Writer::fixUpReferenceRelocatable() // Writer::fixUpReferenceFinal() -// Writer::stubableReferenceKind() +// Writer::stubableReference() // Writer::weakImportReferenceKind() // Writer::GOTReferenceKind() // @@ -86,13 +94,17 @@ 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 @@ -125,7 +137,8 @@ class SegmentInfo { public: SegmentInfo() : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), - fBaseAddress(0), fSize(0), fFixedAddress(false) { fName[0] = '\0'; } + fBaseAddress(0), fSize(0), fFixedAddress(false), + fIndependentAddress(false) { fName[0] = '\0'; } std::vector fSections; char fName[20]; uint32_t fInitProtection; @@ -135,6 +148,7 @@ public: uint64_t fBaseAddress; uint64_t fSize; bool fFixedAddress; + bool fIndependentAddress; }; template @@ -151,12 +165,16 @@ public: 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, - bool createUUID); + bool createUUID, bool canScatter, + ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs); private: typedef typename A::P P; @@ -166,6 +184,7 @@ private: void assignFileOffsets(); void synthesizeStubs(); + void insertDummyStubs(); void partitionIntoSections(); bool addBranchIslands(); bool addPPCBranchIslands(); @@ -174,14 +193,23 @@ private: void createDynamicLinkerCommand(); void createDylibCommands(); void buildLinkEdit(); + const char* getArchString(); + void writeMap(); uint64_t writeAtoms(); void writeNoOps(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; @@ -189,18 +217,21 @@ private: void adjustLinkEditSections(); void buildObjectFileFixups(); void buildExecutableFixups(); + bool preboundLazyPointerType(uint8_t* type); uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const; - bool referenceRequiresRuntimeFixUp(const ObjectFile::Reference* ref, bool slideable) 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(); - bool stubableReferenceKind(uint8_t kind); + uint64_t maxAddress(); + bool stubableReference(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); @@ -208,10 +239,12 @@ private: 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 slideable); + bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&); bool mightNeedPadSegment(); void scanForAbsoluteReferences(); - + bool needsModuleTable(); + void optimizeDylibReferences(); + bool indirectSymbolIsLocal(const ObjectFile::Reference* ref) const; struct DirectLibrary { class ObjectFile::Reader* fLibrary; @@ -235,13 +268,17 @@ private: 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; @@ -259,23 +296,28 @@ private: class SegmentInfo* fPadSegmentInfo; class ObjectFile::Atom* fEntryPoint; class ObjectFile::Atom* fDyldHelper; - std::vector fDirectLibraries; + 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 fStubsMap; std::map fGOTMap; std::vector*> fAllSynthesizedStubs; std::vector fAllSynthesizedStubHelpers; @@ -294,10 +336,15 @@ private: bool fEmitVirtualSections; bool fHasWeakExports; bool fReferencesWeakImports; - bool fSeenFollowOnReferences; + bool fCanScatter; bool fWritableSegmentPastFirst4GB; + bool fNoReExportedDylibs; + bool fBiggerThanTwoGigs; + bool fSlideable; std::map fWeakImportMap; SegmentInfo* fFirstWritableSegment; + ObjectFile::Reader::CpuConstraint fCpuConstraint; + uint32_t fAnonNameIndex; }; @@ -311,13 +358,16 @@ public: 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; @@ -332,7 +382,9 @@ 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 @@ -354,8 +406,8 @@ public: virtual std::vector& getReferences() const { return fgEmptyReferenceList; } virtual bool mustRemainInSection() const { return true; } virtual ObjectFile::Segment& getSegment() const { return fSegment; } - virtual bool requiresFollowOnAtom() const { return false; } 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"; } @@ -422,6 +474,7 @@ public: 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; @@ -449,12 +502,17 @@ template class LoadCommandAtom : public WriterAtom { protected: - LoadCommandAtom(Writer& writer, Segment& segment) : WriterAtom(writer, segment) {} + 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); +private: + uint32_t fOrdinal; + static uint32_t fgCurrentOrdinal; }; +template uint32_t LoadCommandAtom::fgCurrentOrdinal = 0; template class SegmentLoadCommandsAtom : public LoadCommandAtom @@ -487,10 +545,11 @@ public: 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; }; @@ -524,6 +583,19 @@ private: 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 { @@ -544,14 +616,16 @@ class DylibLoadCommandsAtom : public LoadCommandAtom { public: DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info) - : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info) {} + : LoadCommandAtom(writer, Segment::fgTextSegment), fInfo(info), fOptimizedAway(false) {} 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; } private: using WriterAtom::fWriter; typedef typename A::P P; - ExecutableFile::DyLibUsed& fInfo; + ExecutableFile::DyLibUsed fInfo; + bool fOptimizedAway; }; template @@ -630,11 +704,13 @@ class UUIDLoadCommandAtom : public LoadCommandAtom { public: UUIDLoadCommandAtom(Writer& writer) - : LoadCommandAtom(writer, Segment::fgTextSegment), fEmit(false) { ::uuid_generate_random(fUUID);} + : 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 emit() { fEmit = true; } + 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; @@ -642,6 +718,23 @@ private: 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 LoadCommandsPaddingAtom : public WriterAtom { @@ -664,12 +757,19 @@ template class LinkEditAtom : public WriterAtom { public: - LinkEditAtom(Writer& writer) : WriterAtom(writer, Segment::fgLinkEditSegment) {} + 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 { @@ -677,7 +777,6 @@ public: SectionRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "section relocations"; } virtual uint64_t getSize() const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(3); } virtual const char* getSectionName() const { return "._section_relocs"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -692,7 +791,6 @@ public: LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "local relocations"; } virtual uint64_t getSize() const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(3); } virtual const char* getSectionName() const { return "._local_relocs"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -721,7 +819,6 @@ public: ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { } virtual const char* getDisplayName() const { return "external relocations"; } virtual uint64_t getSize() const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(3); } virtual const char* getSectionName() const { return "._extern_relocs"; } virtual void copyRawContent(uint8_t buffer[]) const; private: @@ -734,6 +831,43 @@ struct IndirectEntry { 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 { @@ -751,6 +885,29 @@ private: typedef typename A::P P; }; +template +class ModuleInfoLinkEditAtom : public LinkEditAtom +{ +public: + ModuleInfoLinkEditAtom(Writer& writer) : LinkEditAtom(writer), fModuleNameOffset(0) { } + virtual const char* getDisplayName() const { return "modulel 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: @@ -770,15 +927,12 @@ public: 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 }; - class CStringComparor { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } - }; typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; std::vector fFullBuffers; @@ -907,6 +1061,23 @@ private: }; +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 { @@ -920,21 +1091,20 @@ public: virtual ~WriterReference() {} - virtual bool isTargetUnbound() const { return false; } - virtual bool isFromTargetUnbound() const { return false; } + 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 bool hasFromTarget() const { return (fFromTarget != NULL); } 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 refrence"; } + virtual const char* getDescription() const { return "writer reference"; } virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } private: @@ -948,209 +1118,601 @@ private: -struct ExportSorter +template <> +StubHelperAtom::StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) { - bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) - { - return (strcmp(left->getName(), right->getName()) < 0); - } -}; + writer.fAllSynthesizedStubHelpers.push_back(this); + fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &lazyPointer)); + fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldHelper)); + if ( writer.fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; +} +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 -Writer::Writer(const char* path, Options& options, std::vector& dynamicLibraries) - : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL), - fLoadCommandsSegment(NULL), fPadSegmentInfo(NULL), fPageZeroAtom(NULL), fSymbolTableCount(0), fLargestAtomSize(1), - fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), - fSeenFollowOnReferences(false), fWritableSegmentPastFirst4GB(false), fFirstWritableSegment(NULL) +const char* StubHelperAtom::stubName(const char* name) { - 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); - fFileDescriptor = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fFileDescriptor == -1 ) { - throw "can't open file for writing"; - } - - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - 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)); - 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(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; - } + char* buf; + asprintf(&buf, "%s$stubHelper", name); + return buf; +} - // add extra commmands - uint8_t ordinal = 1; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - 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]; - if ( dylibInfo.options.fBundleLoader ) { - fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL; - } - else if ( dylibInfo.indirect ) { - // find ordinal of direct reader - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { - bool found = false; - for (std::map::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - if ( it->first == dylibInfo.directReader ) { - //fprintf(stderr, "ordinal %d for indirect %s\n", it->second, dylibInfo.reader->getPath()); - fLibraryToOrdinal[dylibInfo.reader] = it->second; - found = true; - break; - } - } - if ( ! found ) - fprintf(stderr, "ld64 warning: ordinal not found for %s, parent %s\n", dylibInfo.reader->getPath(), dylibInfo.directReader != NULL ? dylibInfo.directReader->getPath() : NULL); - } - } - else { - // see if a DylibLoadCommandsAtom has already been created for this install path - bool newDylib = true; - const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); - if ( dylibInfo.options.fInstallPathOverride != NULL ) - dylibInstallPath = dylibInfo.options.fInstallPathOverride; - for (unsigned int seenLib=0; seenLib < i; ++seenLib) { - ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; - if ( !seenDylibInfo.indirect && !seenDylibInfo.options.fBundleLoader ) { - const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); - if ( seenDylibInfo.options.fInstallPathOverride != NULL ) - seenDylibInstallPath = dylibInfo.options.fInstallPathOverride; - if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { - fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; - newDylib = false; - break; - } - } - } - if ( newDylib ) { - // assign new ordinal and check for other paired load commands - fLibraryToOrdinal[dylibInfo.reader] = ordinal++; - fWriterSynthesizedAtoms.push_back(new DylibLoadCommandsAtom(*this, dylibInfo)); - if ( dylibInfo.options.fReExport ) { - // 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())); - } - std::vector& allowableClients = fOptions.allowableClients(); - if ( allowableClients.size() != 0 ) { - 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; - } +// specialize lazy pointer for x86_64 to initially pointer to stub helper +template <> +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedLazyPointers.push_back(this); - //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()); - //} + StubHelperAtom* helper = new StubHelperAtom(writer, target, *this); + fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); } + template -Writer::~Writer() +LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target) { - if ( fFilePath != NULL ) + 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 <> +StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target); + 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) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target); + 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) + : WriterAtom(writer, writer.fOptions.readOnlyx86Stubs() ? Segment::fgROImportSegment : Segment::fgImportSegment), + fTarget(target) +{ + 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) + : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +{ + writer.fAllSynthesizedStubs.push_back(this); + + LazyPointerAtom* lp = new LazyPointerAtom(writer, target); + fReferences.push_back(new WriterReference(2, x86_64::kPCRel32, 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 5; +} + +template <> +uint64_t StubAtom::getSize() const +{ + return 6; +} + +template <> +ObjectFile::Alignment StubAtom::getAlignment() const +{ + // special case x86 fast stubs to be byte aligned + return 0; +} + +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.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; +} + +// 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 "__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), fLoadCommandsSection(NULL), + fLoadCommandsSegment(NULL), fPadSegmentInfo(NULL), fSplitCodeToDataContentAtom(NULL), fModuleInfoAtom(NULL), + fPageZeroAtom(NULL), fSymbolTableCount(0), fLargestAtomSize(1), + fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), + fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false), fSlideable(false), + fFirstWritableSegment(NULL), fAnonNameIndex(1000) +{ + // for UNIX conformance, error if file exists and is not writable + if ( (access(path, F_OK) == 0) && (access(path, W_OK) == -1) ) + throwf("can't write output file: %s", path); + + 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); + fFileDescriptor = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fFileDescriptor == -1 ) { + throwf("can't open output file for writing: %s", path); + } + + 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; + uint8_t ordinal = 1; + switch ( fOptions.outputKind() ) { + case Options::kDynamicExecutable: + 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; @@ -1165,8 +1727,16 @@ template bool Writer::mightNeedPadSegment() { return false; } template ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name) { - if ( (fOptions.outputKind() == Options::kObjectFile) - || (fOptions.undefinedTreatment() != Options::kUndefinedError) ) + 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; @@ -1191,22 +1761,35 @@ uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib) 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, - bool createUUID) + bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, + bool biggerThanTwoGigs) { fAllAtoms = &atoms; fStabs = &stabs; fEntryPoint = entryPointAtom; fDyldHelper = dyldHelperAtom; + fCanScatter = canScatter; + fCpuConstraint = cpuConstraint; + fBiggerThanTwoGigs = biggerThanTwoGigs; try { // Set for create UUID if (createUUID) - fUUIDAtom->emit(); + fUUIDAtom->generate(); + + // remove uneeded dylib load commands + optimizeDylibReferences(); // check for mdynamic-no-pic codegen which force code into low 4GB scanForAbsoluteReferences(); @@ -1230,6 +1813,9 @@ uint64_t Writer::write(std::vector& atoms, // build symbol table and relocations buildLinkEdit(); + // write map file if requested + writeMap(); + // write everything return writeAtoms(); } catch (...) { @@ -1259,9 +1845,29 @@ uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom) // 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); @@ -1279,7 +1885,8 @@ void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent entry->set_n_sect(sectionIndex); // the __mh_execute_header is magic and must be an absolute symbol - if ( (fOptions.outputKind() == Options::kDynamicExecutable) && (sectionIndex==0) + if ( (sectionIndex==0) + && (fOptions.outputKind() == Options::kDynamicExecutable) && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )) entry->set_n_type(N_EXT | N_ABS); @@ -1294,18 +1901,27 @@ void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent entry->set_n_desc(desc); // 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)); + 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 - entry->set_n_type(N_UNDF | N_EXT); - if ( fOptions.keepPrivateExterns() - && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (fOptions.outputKind() == Options::kObjectFile) ) + 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); @@ -1313,15 +1929,26 @@ void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent uint16_t desc = 0; if ( fOptions.outputKind() != Options::kObjectFile ) { // set n_desc ( high byte is library ordinal, low byte is reference type ) - desc = REFERENCE_FLAG_UNDEFINED_LAZY; // FIXME + std::map::iterator pos = fStubsMap.find(atom); + if ( pos != fStubsMap.end() ) + 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; + // if alignment does not match size, then record the custom alignment + if ( align != __builtin_ctz(atom->getSize()) ) + SET_COMM_ALIGN(desc, align); + } if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) desc |= REFERENCED_DYNAMICALLY; if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { @@ -1337,11 +1964,23 @@ void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist

* ent 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); @@ -1362,17 +2001,69 @@ void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist

* entr entry->set_n_desc(desc); // 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)); + 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]; - entry->set_n_strx(this->fStringsAtom->add(atom->getName())); if ( &atoms == &fExportedAtoms ) { this->setExportNlist(atom, entry); } @@ -1385,15 +2076,37 @@ void Writer::setNlistRange(std::vector& atoms, uint3 } } +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(); + fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size(); fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; - fSymbolTableExportCount = fExportedAtoms.size(); + fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size(); fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; fSymbolTableImportCount = fImportedAtoms.size(); @@ -1402,10 +2115,23 @@ void Writer::buildSymbolTable() 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, fSymbolTableLocalCount); - setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fSymbolTableExportCount); + 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(); } @@ -1413,16 +2139,24 @@ void Writer::buildSymbolTable() template bool Writer::shouldExport(const ObjectFile::Atom& atom) const { - if ( atom.getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - return false; - switch ( atom.getScope() ) { - case ObjectFile::Atom::scopeGlobal: - return true; - case ObjectFile::Atom::scopeLinkageUnit: - return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ); - default: + 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 @@ -1433,39 +2167,62 @@ void Writer::collectExportedAndImportedAndLocalAtoms() fImportedAtoms.reserve(100); fExportedAtoms.reserve(atomCount/2); fLocalSymbolAtoms.reserve(atomCount); - for (int i=0; i < atomCount; ++i) { - ObjectFile::Atom* atom = (*fAllAtoms)[i]; + 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 - //printf("collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); + //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.makeTentativeDefinitionsReal() ) { + 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 ( !fOptions.stripLocalSymbols() - && (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ) + 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) { + 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.outputKind() == Options::kStaticExecutable ) { + 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(), ExportSorter()); + 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(), ExportSorter()); + std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter()); } @@ -1603,6 +2360,37 @@ uint32_t Writer::symbolIndex(ObjectFile::Atom& atom) } +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: + return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit); + case ObjectFile::Atom::kExternalDefinition: + case ObjectFile::Atom::kExternalWeakDefinition: + return shouldExport(target); + } + return false; +} + template void Writer::buildFixups() { @@ -1620,7 +2408,7 @@ template <> uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) { ObjectFile::Atom& target = ref->getTarget(); - bool external = (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); + 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; @@ -1640,7 +2428,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc1.set_r_length(3); reloc1.set_r_extern(external); reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case x86_64::kPointerDiff32: @@ -1661,20 +2449,22 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); reloc2.set_r_extern(fromExternal); reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); + 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.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case x86_64::kPCRel32: @@ -1684,7 +2474,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc1.set_r_length(2); reloc1.set_r_extern(external); reloc1.set_r_type(X86_64_RELOC_SIGNED); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case x86_64::kPCRel32_1: @@ -1694,7 +2484,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc1.set_r_length(2); reloc1.set_r_extern(external); reloc1.set_r_type(X86_64_RELOC_SIGNED_1); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case x86_64::kPCRel32_2: @@ -1704,7 +2494,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc1.set_r_length(2); reloc1.set_r_extern(external); reloc1.set_r_type(X86_64_RELOC_SIGNED_2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case x86_64::kPCRel32_4: @@ -1714,7 +2504,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc1.set_r_length(2); reloc1.set_r_extern(external); reloc1.set_r_type(X86_64_RELOC_SIGNED_4); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case x86_64::kPCRel32GOT: @@ -1725,7 +2515,7 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc1.set_r_length(2); reloc1.set_r_extern(external); reloc1.set_r_type(X86_64_RELOC_GOT); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case x86_64::kPCRel32GOTLoad: @@ -1736,8 +2526,13 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Ref reloc1.set_r_length(2); reloc1.set_r_extern(external); reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; + + case x86_64::kDtraceTypeReference: + case x86_64::kDtraceProbe: + // generates no relocs + return 0; } return 0; } @@ -1747,18 +2542,7 @@ template <> uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) { ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = false; - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - isExtern = false; - break; - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - isExtern = shouldExport(target); - break; - } + bool isExtern = this->makesExternalRelocatableReference(target); uint32_t symbolIndex = 0; if ( isExtern ) symbolIndex = this->symbolIndex(target); @@ -1769,6 +2553,11 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere 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) ) + fprintf(stderr, "ld: warning section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s\n", + target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); + switch ( kind ) { case x86::kNoFixUp: @@ -1795,15 +2584,19 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere reloc1.set_r_extern(isExtern); reloc1.set_r_type(GENERIC_RELOC_VANILLA); } - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; + case x86::kPointerDiff16: case x86::kPointerDiff: { - pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); + //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(2); + 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 @@ -1812,22 +2605,28 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere 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_length( (kind==x86::kPointerDiff) ? 2 : 1 ); + sreloc2->set_r_type(GENERIC_RELOC_PAIR); sreloc2->set_r_address(0); - sreloc2->set_r_value(fromAddr); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + //if ( &ref->getFromTarget() == &ref->getTarget() ) + 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::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(2); + sreloc1->set_r_length( (kind==x86::kPCRel16) ? 1 : 2); sreloc1->set_r_type(GENERIC_RELOC_VANILLA); sreloc1->set_r_address(address); sreloc1->set_r_value(target.getAddress()); @@ -1836,12 +2635,17 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere 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_length( (kind==x86::kPCRel16) ? 1 : 2); reloc1.set_r_extern(isExtern); reloc1.set_r_type(GENERIC_RELOC_VANILLA); } - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; + + case x86::kDtraceTypeReference: + case x86::kDtraceProbe: + // generates no relocs + return 0; } return 0; @@ -1849,6 +2653,13 @@ uint32_t Writer::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Refere + +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 <> uint8_t Writer::getRelocPointerSize() { @@ -1881,19 +2692,7 @@ template uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref) { ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = false; - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - isExtern = false; - break; - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - isExtern = shouldExport(target); - break; - } - + bool isExtern = this->makesExternalRelocatableReference(target); uint32_t symbolIndex = 0; if ( isExtern ) symbolIndex = this->symbolIndex(target); @@ -1932,9 +2731,10 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: reloc1.set_r_extern(isExtern); reloc1.set_r_type(GENERIC_RELOC_VANILLA); } - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; + case A::kPointerDiff16: case A::kPointerDiff32: case A::kPointerDiff64: { @@ -1942,23 +2742,25 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); sreloc1->set_r_scattered(true); sreloc1->set_r_pcrel(false); - sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : 3); + sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1)); sreloc1->set_r_type(ref->getTargetOffset() != 0 ? PPC_RELOC_LOCAL_SECTDIFF : PPC_RELOC_SECTDIFF); sreloc1->set_r_address(address); sreloc1->set_r_value(toAddr); sreloc2->set_r_scattered(true); sreloc2->set_r_pcrel(false); - sreloc2->set_r_length( (kind == A::kPointerDiff32) ? 2 : 3); + sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1)); sreloc2->set_r_type(PPC_RELOC_PAIR); sreloc2->set_r_address(0); sreloc2->set_r_value(fromAddr); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + 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 ) @@ -1978,7 +2780,7 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: sreloc1->set_r_address(address); sreloc1->set_r_value(target.getAddress()); } - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case A::kBranch14: @@ -2001,7 +2803,7 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: sreloc1->set_r_address(address); sreloc1->set_r_value(target.getAddress()); } - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); return 1; case A::kPICBaseLow16: @@ -2019,10 +2821,10 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: sreloc2->set_r_pcrel(false); sreloc2->set_r_length(2); sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(((toAddr-fromAddr) >> 16)); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); sreloc2->set_r_value(fromAddr); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); return 2; } @@ -2042,8 +2844,8 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: sreloc2->set_r_type(PPC_RELOC_PAIR); sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); sreloc2->set_r_value(fromAddr); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); return 2; } @@ -2079,8 +2881,8 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: reloc2.set_r_length(2); reloc2.set_r_extern(false); reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); return 2; } @@ -2115,8 +2917,8 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: reloc2.set_r_length(2); reloc2.set_r_extern(false); reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc2); + fSectionRelocs.push_back(reloc1); return 2; } @@ -2154,16 +2956,35 @@ uint32_t Writer::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile:: reloc2.set_r_length(2); reloc2.set_r_extern(false); reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc2); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + 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() { @@ -2190,8 +3011,6 @@ void Writer::buildObjectFileFixups() const int refCount = refs.size(); for (int l=0; l < refCount; ++l) { ObjectFile::Reference* ref = refs[l]; - if ( ref->getKind() == A::kFollowOn ) - fSeenFollowOnReferences = true; if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs ) { uint32_t offsetInSection = atom->getSectionOffset(); uint32_t indexInSection = offsetInSection / atom->getSize(); @@ -2202,13 +3021,17 @@ void Writer::buildObjectFileFixups() undefinedSymbolIndex = this->symbolIndex(stubTargetTarget); //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex); } - else { - // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table - if ( curSection->fAllNonLazyPointers && (ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) ) + 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()); @@ -2230,7 +3053,7 @@ void Writer::buildObjectFileFixups() reloc1.set_r_length(); reloc1.set_r_extern(isExtern); reloc1.set_r_type(GENERIC_RELOC_VANILLA); - fSectionRelocs.insert(fSectionRelocs.begin(), reloc1); + fSectionRelocs.push_back(reloc1); ++relocIndex; } } @@ -2238,7 +3061,7 @@ void Writer::buildObjectFileFixups() relocIndex += this->addObjectRelocs(atom, ref); } } - else if ( ref->getKind() != A::kNoFixUp ) { + else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) { relocIndex += this->addObjectRelocs(atom, ref); } } @@ -2248,7 +3071,10 @@ void Writer::buildObjectFileFixups() } } - // now reverse reloc entries + // 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; @@ -2262,14 +3088,14 @@ void Writer::buildObjectFileFixups() } template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable) +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) { switch ( ref.getKind() ) { case ppc::kAbsLow16: case ppc::kAbsLow14: case ppc::kAbsHigh16: case ppc::kAbsHigh16AddLow: - if ( slideable ) + if ( fSlideable ) return true; } return false; @@ -2277,42 +3103,43 @@ bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& re template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable) +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) { switch ( ref.getKind() ) { case ppc::kAbsLow16: case ppc::kAbsLow14: case ppc::kAbsHigh16: case ppc::kAbsHigh16AddLow: - if ( slideable ) + if ( fSlideable ) return true; } return false; } template <> -bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref, bool slideable) +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) { if ( ref.getKind() == x86::kAbsolute32 ) { switch ( ref.getTarget().getDefinitionKind() ) { case ObjectFile::Atom::kTentativeDefinition: case ObjectFile::Atom::kRegularDefinition: - // illegal in dylibs/bundles, until we support TEXT relocs - return slideable; case ObjectFile::Atom::kWeakDefinition: - // illegal if an exported weak symbol, until we support TEXT relocs - return this->shouldExport(ref.getTarget()); + // 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, bool slideable) +bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) { return false; } @@ -2321,19 +3148,28 @@ bool Writer::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& template typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const { - const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable); - 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() ) + 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 - if ( this->shouldExport(target) && - ((fOptions.nameSpace() == Options::kFlatNameSpace) + else if ( this->shouldExport(target) && + ((fOptions.nameSpace() == Options::kFlatNameSpace) || (fOptions.nameSpace() == Options::kForceFlatNameSpace) - || fOptions.interposable()) ) + || fOptions.interposable()) + && (target.getName() != NULL) + && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // return kRelocExternal; - else if ( slideable ) + else if ( fSlideable ) return kRelocInternal; else return kRelocNone; @@ -2341,13 +3177,15 @@ typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(cons // all calls to global weak definitions get indirected if ( this->shouldExport(target) ) return kRelocExternal; - else if ( slideable ) + 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; } @@ -2356,8 +3194,11 @@ 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 base address - uint64_t result = address - fOptions.baseAddress(); + // 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()); @@ -2389,7 +3230,7 @@ uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const O uint64_t result; bool badFor10_4 = false; if ( fWritableSegmentPastFirst4GB ) { - if ( fOptions.macosxVersionMin() < Options::k10_5 ) + if ( fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5 ) badFor10_4 = true; result = address - fFirstWritableSegment->fBaseAddress; if ( result > 0xFFFFFFFF ) { @@ -2398,8 +3239,8 @@ uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const O } } else { - result = address - fOptions.baseAddress(); - if ( (fOptions.macosxVersionMin() < Options::k10_5) && (result > 0x7FFFFFFF) ) + result = address - fSegmentInfos[0]->fBaseAddress; + if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) && (result > 0x7FFFFFFF) ) badFor10_4 = true; } if ( badFor10_4 ) { @@ -2410,10 +3251,15 @@ uint64_t Writer::relocAddressInFinalLinkedImage(uint64_t address, const O } +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 void Writer::buildExecutableFixups() { - const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable); fIndirectTableAtom->fTable.reserve(50); // minimize reallocations std::vector& segmentInfos = fSegmentInfos; const int segCount = segmentInfos.size(); @@ -2423,7 +3269,7 @@ void Writer::buildExecutableFixups() const int sectionCount = sectionInfos.size(); for(int j=0; j < sectionCount; ++j) { SectionInfo* curSection = sectionInfos[j]; - //fprintf(stderr, "starting section %p\n", curSection->fSectionName); + //fprintf(stderr, "starting section %s\n", curSection->fSectionName); std::vector& sectionAtoms = curSection->fAtoms; if ( ! curSection->fAllZeroFill ) { if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) @@ -2454,24 +3300,38 @@ void Writer::buildExecutableFixups() IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; //fprintf(stderr,"fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize()); fIndirectTableAtom->fTable.push_back(entry); - if ( slideable && curSection->fAllLazyPointers ) { - // if this is a dylib/bundle, need vanilla internal relocation to fix up binding handler if image slides - macho_relocation_info

pblaReloc; - 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); - pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - pblaReloc.set_r_symbolnum(sectionNum); - pblaReloc.set_r_pcrel(false); - pblaReloc.set_r_length(); - pblaReloc.set_r_extern(false); - pblaReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(pblaReloc); + if ( curSection->fAllLazyPointers ) { + uint8_t preboundLazyType; + if ( fOptions.prebind() && (fDyldHelper != NULL) && 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 ) { - if ( slideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 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", atom->getDisplayName(), atom->getFile()->getPath()); } @@ -2510,13 +3370,13 @@ void Writer::buildExecutableFixups() break; } } - else if ( this->illegalRelocInFinalLinkedImage(*ref, slideable) ) { + else if ( this->illegalRelocInFinalLinkedImage(*ref) ) { throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); } } if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) { ObjectFile::Atom* stubTarget = ((StubAtom*)atom)->getTarget(); - uint32_t undefinedSymbolIndex = this->symbolIndex(*stubTarget); + 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; @@ -2528,6 +3388,137 @@ void Writer::buildExecutableFixups() } } } + 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::kPointer: + case ppc::kPointerWeakImport: + case ppc::kPICBaseLow16: + case ppc::kPICBaseLow14: + // ignore + break; + default: + fprintf(stderr, "ld: warning codegen with reference kind %d in %s prevents image from loading in dyld shared cache\n", 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::kPointer: + case ppc64::kPointerWeakImport: + case ppc64::kPICBaseLow16: + case ppc64::kPICBaseLow14: + // ignore + break; + default: + fprintf(stderr, "ld: warning codegen with reference kind %d in %s prevents image from loading in dyld shared cache\n", 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::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: + fprintf(stderr, "ld: warning codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache\n", 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::kPointer: + // ignore + break; + default: + fprintf(stderr, "ld: warning codegen in %s with kind %d prevents image from loading in dyld shared cache\n", atom->getDisplayName(), ref->getKind()); + 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"; } @@ -2565,56 +3556,233 @@ void Writer::writeNoOps(uint32_t from, uint32_t to) ::pwrite(fFileDescriptor, &x86Nop, 1, 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; +} + + +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 +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 { + fprintf(stderr, "ld: warning could not write map file: %s\n", fOptions.generatedMapPath()); + } + } +} + template uint64_t Writer::writeAtoms() { + // try to allocate buffer for entire output file content + 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 ) { + atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; + streaming = true; + } + uint32_t size = 0; uint32_t end = 0; - uint8_t* buffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; - std::vector& segmentInfos = fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - bool isTextSeg = ((curSegment->fInitProtection & VM_PROT_EXECUTE) != 0); + 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; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; + 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 %d atoms for section %s\n", (int)sectionAtoms.size(), curSection->fSectionName); if ( ! curSection->fAllZeroFill ) { - const int atomCount = sectionAtoms.size(); end = curSection->fFileOffset; bool needsNops = isTextSeg && (strcmp(curSection->fSectionName, "__cstring") != 0); - for (int k=0; k < atomCount; ++k) { - ObjectFile::Atom* atom = sectionAtoms[k]; + 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) ) { - uint32_t offset = curSection->fFileOffset + atom->getSectionOffset(); - if ( offset != end ) { + && (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 - writeNoOps(end, offset); + if ( streaming ) + writeNoOps(end, fileOffset); + else + copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]); } - else { + else if ( streaming ) { // zero fill gaps - if ( (offset-end) == 4 ) { + if ( (fileOffset-end) == 4 ) { uint32_t zero = 0; ::pwrite(fFileDescriptor, &zero, 4, end); } else { uint8_t zero = 0x00; - for (uint32_t p=end; p < offset; ++p) + for (uint32_t p=end; p < fileOffset; ++p) ::pwrite(fFileDescriptor, &zero, 1, p); } } } uint64_t atomSize = atom->getSize(); - if ( atomSize > fLargestAtomSize ) { - throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%X > 0x%llX", - atom->getDisplayName(), atomSize, fLargestAtomSize); + 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); } - end = offset+atomSize; + uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset]; + end = fileOffset+atomSize; // copy raw bytes atom->copyRawContent(buffer); // apply any fix-ups @@ -2638,15 +3806,66 @@ uint64_t Writer::writeAtoms() 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", - // offset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath()); - // write out - ::pwrite(fFileDescriptor, buffer, atom->getSize(), offset); + // fileOffset, end, atom->getAddress(), atom->getSize(), atom->getDisplayName(), atom->getFile()->getPath()); + if ( streaming ) { + // write out + ::pwrite(fFileDescriptor, buffer, atomSize, fileOffset); + } + else { + if ( (fileOffset + atomSize) > size ) + size = fileOffset + atomSize; + } } } } } } - delete [] buffer; + + // 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(fFileDescriptor, 0, SEEK_SET); + ssize_t len; + while ( (len = ::read(fFileDescriptor, 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(fFileDescriptor, atomBuffer, fUUIDAtom->getSize(), uuidOffset); + } + else { + // if output file fit in memory, just genrate an md5 hash in memory + CC_MD5(wholeBuffer, size, digest); + fUUIDAtom->setContent(digest); + uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); + fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]); + } + } + + // finish up + if ( streaming ) { + delete [] atomBuffer; + } + else { + // write whole output file in one chunk + ::pwrite(fFileDescriptor, wholeBuffer, size, 0); + delete [] wholeBuffer; + } + close(fFileDescriptor); return end; } @@ -2656,9 +3875,12 @@ template <> void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const { uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - const int64_t bl_twoGigLimit = 0x7FFFFFFF; + uint8_t* dtraceProbeSite; + const int64_t kTwoGigLimit = 0x7FFFFFFF; + const int64_t kSixtyFourKiloLimit = 0x7FFF; int64_t displacement; - switch ( (x86::ReferenceKinds)(ref->getKind()) ) { + x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); + switch ( kind ) { case x86::kNoFixUp: case x86::kFollowOn: // do nothing @@ -2667,8 +3889,27 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob case x86::kPointer: { if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - // external realocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); + 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 realocation ==> pointer contains addend + LittleEndian::set32(*fixUp, ref->getTargetOffset()); + } } else { // pointer contains target address @@ -2678,11 +3919,36 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob } break; case x86::kPointerDiff: - LittleEndian::set32(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + 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: displacement = 0; switch ( ref->getTarget().getDefinitionKind() ) { case ObjectFile::Atom::kRegularDefinition: @@ -2695,12 +3961,24 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob case ObjectFile::Atom::kTentativeDefinition: displacement = 0; break; + case ObjectFile::Atom::kAbsoluteSymbol: + displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); + break; } - if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_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"; + 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); } - LittleEndian::set32(*fixUp, (int32_t)displacement); break; case x86::kAbsolute32: switch ( ref->getTarget().getDefinitionKind() ) { @@ -2715,18 +3993,31 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const Ob // external realocation ==> 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; uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - bool isExternal = ( (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && shouldExport(ref->getTarget()) ); - switch ( (x86::ReferenceKinds)(ref->getKind()) ) { + bool isExtern = this->makesExternalRelocatableReference(ref->getTarget()); + int64_t displacement; + x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); + switch ( kind ) { case x86::kNoFixUp: case x86::kFollowOn: // do nothing @@ -2735,40 +4026,68 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, co case x86::kPointerWeakImport: case x86::kAbsolute32: { - if ( isExternal ) { + if ( isExtern ) { // external realocation ==> pointer contains addend LittleEndian::set32(*fixUp, ref->getTargetOffset()); } - else { - // internal relocation - if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { - // pointer contains target address + 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 { - // pointer contains addend - LittleEndian::set32(*fixUp, 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: - LittleEndian::set32(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); + 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::kPCRel16: case x86::kPCRel32: case x86::kPCRel32WeakImport: - int64_t displacement = 0; - if ( isExternal ) - displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - else - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - const int64_t bl_twoGigLimit = 0x7FFFFFFF; - if ( (displacement > bl_twoGigLimit) || (displacement < (-bl_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"; + 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::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 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); + } } - LittleEndian::set32(*fixUp, (int32_t)displacement); + break; + case x86::kDtraceProbe: + case x86::kDtraceTypeReference: + // nothing to fix up break; } } @@ -2778,6 +4097,7 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, 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: @@ -2810,6 +4130,17 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const 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::kPCRel32: @@ -2818,14 +4149,15 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const case x86_64::kPCRel32_4: case x86_64::kPCRel32GOT: case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: 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"; @@ -2843,11 +4175,34 @@ void Writer::fixUpReferenceFinal(const ObjectFile::Reference* ref, const 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()); + 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; } } @@ -2855,7 +4210,7 @@ template <> void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const { const int64_t twoGigLimit = 0x7FFFFFFF; - bool external = (ref->getTarget().getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); + bool external = this->makesExternalRelocatableReference(ref->getTarget()); uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; int64_t displacement = 0; int32_t temp32; @@ -2887,6 +4242,8 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, 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: @@ -2925,6 +4282,10 @@ void Writer::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, // 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; } } @@ -2961,22 +4322,23 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O uint32_t instruction; uint32_t newInstruction; int64_t displacement; - uint64_t targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); + 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; - - if ( finalLinkedImage ) - relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); - else - relocateableExternal = ( (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && shouldExport(ref->getTarget()) ); - + 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: @@ -2987,27 +4349,65 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O { //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); if ( finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllLazyPointers ) { - // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) - if ( fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - P::setP(*fixUpPointer, fDyldHelper->getAddress()); + if ( fOptions.prebind() ) { + 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: + // prebound lazy pointer to withing this dylib ==> pointer contains address + P::setP(*fixUpPointer, targetAddr); + break; + case ObjectFile::Atom::kAbsoluteSymbol: + break; + } + } + else { + // lazy-symbol ==> pointer contains address of dyld_stub_binding_helper (stored in "from" target) + if ( fDyldHelper == NULL ) + throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + P::setP(*fixUpPointer, fDyldHelper->getAddress()); + } } else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content - if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) + // 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 ) { - // external realocation ==> pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); + 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 realocation ==> 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", target.getDisplayName(), target.getAddress()); + //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr); P::setP(*fixUpPointer, targetAddr); } else { @@ -3023,6 +4423,35 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O 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: { @@ -3037,9 +4466,9 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O 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 in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().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); @@ -3074,7 +4503,7 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O } break; case A::kPICBaseLow16: - picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset(); + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); displacement = targetAddr - picBaseAddr; if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) throw "32-bit pic-base out of range"; @@ -3084,7 +4513,7 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O BigEndian::set32(*fixUp, newInstruction); break; case A::kPICBaseLow14: - picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset(); + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); displacement = targetAddr - picBaseAddr; if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) throw "32-bit pic-base out of range"; @@ -3096,7 +4525,7 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O BigEndian::set32(*fixUp, newInstruction); break; case A::kPICBaseHigh16: - picBaseAddr = inAtom->getAddress() + ref->getFromTargetOffset(); + picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); displacement = targetAddr - picBaseAddr; if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) throw "32-bit pic-base out of range"; @@ -3108,7 +4537,7 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O BigEndian::set32(*fixUp, newInstruction); break; case A::kAbsLow16: - if ( relocateableExternal ) + if ( relocateableExternal && !finalLinkedImage ) targetAddr -= ref->getTarget().getAddress(); instructionLowHalf = (targetAddr & 0xFFFF); instruction = BigEndian::get32(*fixUp); @@ -3116,7 +4545,7 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O BigEndian::set32(*fixUp, newInstruction); break; case A::kAbsLow14: - if ( relocateableExternal ) + if ( relocateableExternal && !finalLinkedImage ) targetAddr -= ref->getTarget().getAddress(); if ( (targetAddr & 0x3) != 0 ) throw "bad address for absolute lo14 instruction fix-up"; @@ -3126,49 +4555,164 @@ void Writer::fixUpReference_powerpc(const ObjectFile::Reference* ref, const O BigEndian::set32(*fixUp, newInstruction); break; case A::kAbsHigh16: - if ( relocateableExternal ) - targetAddr -= ref->getTarget().getAddress(); + 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 ) - targetAddr -= ref->getTarget().getAddress(); + 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::stubableReferenceKind(uint8_t kind) -{ - return (kind == ppc::kBranch24 || kind == ppc::kBranch24WeakImport); +bool Writer::stubableReference(const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (ppc::ReferenceKinds)kind ) { + case ppc::kNoFixUp: + case ppc::kFollowOn: + 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: + return true; + case ObjectFile::Atom::kTentativeDefinition: + case ObjectFile::Atom::kRegularDefinition: + case ObjectFile::Atom::kWeakDefinition: + case ObjectFile::Atom::kAbsoluteSymbol: + return false; + } + break; + } + return false; } + template <> -bool Writer::stubableReferenceKind(uint8_t kind) -{ - return (kind == ppc64::kBranch24 || kind == ppc64::kBranch24WeakImport); +bool Writer::stubableReference(const ObjectFile::Reference* ref) +{ + uint8_t kind = ref->getKind(); + switch ( (ppc64::ReferenceKinds)kind ) { + case ppc::kNoFixUp: + case ppc::kFollowOn: + 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::stubableReferenceKind(uint8_t kind) +bool Writer::stubableReference(const ObjectFile::Reference* ref) { + uint8_t kind = ref->getKind(); return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); } template <> -bool Writer::stubableReferenceKind(uint8_t kind) +bool Writer::stubableReference(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) { @@ -3233,13 +4777,145 @@ 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; +} + + + +// 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::scanForAbsoluteReferences() +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) + uint32_t newOrdinal = 0; + for (std::map::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first <= fLibraryToOrdinal.size() ) + fLibraryToOrdinal[it->second] = ++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() +{ + // x86_64 codegen never has absolute references +} + +template <> +void Writer::scanForAbsoluteReferences() +{ + // when linking -pie verify there are no absolute addressing + if ( fOptions.positionIndependentExecutable() ) { + 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\n", atom->getDisplayName(), atom->getFile()->getPath()); + return; + } + } + } + } +} + +template <> +void Writer::scanForAbsoluteReferences() { - // do nothing + // when linking -pie verify there are no absolute addressing + if ( fOptions.positionIndependentExecutable() ) { + 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\n", atom->getDisplayName(), atom->getFile()->getPath()); + return; + } + } + } + } } + // for ppc64 look for any -mdynamic-no-pic codegen template <> void Writer::scanForAbsoluteReferences() @@ -3256,7 +4932,7 @@ void Writer::scanForAbsoluteReferences() case ppc64::kAbsLow14: case ppc64::kAbsHigh16: case ppc64::kAbsHigh16AddLow: - //fprintf(stderr, "found -mdyanmic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); + //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"); @@ -3269,14 +4945,41 @@ void Writer::scanForAbsoluteReferences() } +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))); //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::kStaticExecutable: case Options::kObjectFile: // these output kinds never have stubs return; + case Options::kStaticExecutable: case Options::kDyld: case Options::kDynamicLibrary: case Options::kDynamicBundle: @@ -3291,68 +4994,98 @@ void Writer::synthesizeStubs() std::vector& references = atom->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* ref = *rit; - 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()); - 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; + 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()); + 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; + } + } + } + } + // create stubs as needed + if ( this->stubableReference(ref) + && (ref->getTargetOffset() == 0) + && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { + ObjectFile::Atom* stub = NULL; + std::map::iterator pos = fStubsMap.find(&target); + if ( pos == fStubsMap.end() ) { + stub = new StubAtom(*this, target); + fStubsMap[&target] = stub; + } + else { + stub = pos->second; + } + // alter reference to use stub instead + ref->setTarget(*stub, 0); + } + // 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()); } } - } - } - // create stubs as needed - if ( this->stubableReferenceKind(ref->getKind()) - && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { - ObjectFile::Atom* stub = NULL; - std::map::iterator pos = fStubsMap.find(&target); - if ( pos == fStubsMap.end() ) { - stub = new StubAtom(*this, target); - fStubsMap[&target] = stub; - } - else { - stub = pos->second; - } - // alter reference to use stub instead - ref->setTarget(*stub, 0); - } - // create GOT slots (non-lazy pointers) as needed - else if ( this->GOTReferenceKind(ref->getKind()) ) { - 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 stubs (x86 only) + this->insertDummyStubs(); // sort lazy pointers + std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter()); // add stubs to fAllAtoms if ( fAllSynthesizedStubs.size() != 0 ) { @@ -3428,7 +5161,8 @@ void Writer::synthesizeStubs() if ( nextSection != curSection ) { if ( (prevAtom != NULL) && ((strcmp(prevAtom->getSectionName(), "__dyld") == 0) - || ((fOptions.outputKind() == Options::kDyld) && (strcmp(prevAtom->getSectionName(), "__data") == 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; @@ -3442,6 +5176,29 @@ void Writer::synthesizeStubs() 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; + } + } + } + } + } @@ -3455,6 +5212,7 @@ void Writer::partitionIntoSections() 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) { @@ -3472,11 +5230,12 @@ void Writer::partitionIntoSections() strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; currentSectionInfo->fAllZeroFill = atom->isZeroFill(); - currentSectionInfo->fVirtualSection = ( (currentSectionInfo->fSectionName[0] == '.') || - (oneSegmentCommand && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition)) && !fOptions.makeTentativeDefinitionsReal() ); + 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) ) { @@ -3489,13 +5248,24 @@ void Writer::partitionIntoSections() 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 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(); @@ -3527,6 +5297,8 @@ void Writer::partitionIntoSections() 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) ) @@ -3535,9 +5307,15 @@ void Writer::partitionIntoSections() currentSectionInfo->fAllStubs = true; if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__symbol_stub") == 0) ) currentSectionInfo->fAllStubs = true; - if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) + 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 + } curSection = atom->getSection(); + if ( currentSectionInfo->fAllNonLazyPointers || currentSectionInfo->fAllLazyPointers + || currentSectionInfo->fAllStubs || currentSectionInfo->fAllStubs ) { + fSymbolTableCommands->needDynamicTable(); + } } // any non-zero fill atoms make whole section marked not-zero-fill if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) @@ -3546,7 +5324,7 @@ void Writer::partitionIntoSections() 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 ) + if ( currentSectionInfo->fAlignment < atomAlign ) currentSectionInfo->fAlignment = atomAlign; // calculate section offset for this atom uint64_t offset = currentSectionInfo->fSize; @@ -3568,6 +5346,18 @@ void Writer::partitionIntoSections() 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; + } } @@ -3661,13 +5451,13 @@ bool Writer::addPPCBranchIslands() bool result = false; // Can only possibly need branch islands if __TEXT segment > 16M if ( fLoadCommandsSegment->fSize > 16000000 ) { - if ( log) fprintf(stderr, "ld64: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); - const uint32_t kBetweenRegions = 15000000; // place regions of islands every 15MB in __text section + 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, "ld64: checking for branch islands, __text section size=%llu\n", textSection->fSize); + if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize); break; } } @@ -3752,7 +5542,7 @@ bool Writer::addPPCBranchIslands() // insert islands into __text section and adjust section offsets if ( islandCount > 0 ) { - if ( log) fprintf(stderr, "ld64: %u branch islands required\n", islandCount); + 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; @@ -3783,9 +5573,9 @@ bool Writer::addPPCBranchIslands() if ( atomSlide != 0 ) atom->setSectionOffset(atom->getSectionOffset()+atomSlide); } + sectionOffset = textSection->fSize+atomSlide; // put any remaining islands at end of __text section if ( regionIndex < kIslandRegionsCount ) { - sectionOffset = textSection->fSize; std::vector* regionIslands = ®ionsIslands[regionIndex]; for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { ObjectFile::Atom* islandAtom = *rit; @@ -3830,16 +5620,16 @@ void Writer::adjustLoadCommandsAndPadding() 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 - 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; - } paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096); } else if ( fOptions.outputKind() == Options::kObjectFile ) { @@ -3847,22 +5637,31 @@ void Writer::adjustLoadCommandsAndPadding() paddingSize = 0; } else { - // calculate max padding to keep segment size same, but all free space at end of load commands - uint64_t totalSize = 0; - uint64_t worstCaseAlignmentPadding = 0; - for(int j=0; j < sectionCount; ++j) { + // 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]; - totalSize += curSection->fSize; - if ( j != 0 ) // don't count aligment of mach_header which is page-aligned - worstCaseAlignmentPadding += (1 << curSection->fAlignment) - 1; + addr -= curSection->fSize; + addr = addr & (0 - (1 << curSection->fAlignment)); + if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) { + addr -= totalSizeOfHeaderAndLoadCommands; + paddingSize = addr % 4096; + break; + } } - uint64_t segmentSize = ((totalSize+worstCaseAlignmentPadding+4095) & (-4096)); - // don't know exactly how it will layout, but we can inflate padding atom this big and still keep aligment constraints - paddingSize = segmentSize - (totalSize+worstCaseAlignmentPadding); // if command line requires more padding than this - if ( paddingSize < fOptions.minimumHeaderPad() ) { - int extraPages = (fOptions.minimumHeaderPad() - paddingSize + 4095)/4096; + 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; } } @@ -3884,11 +5683,32 @@ void Writer::assignFileOffsets() 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; @@ -3923,7 +5743,7 @@ void Writer::assignFileOffsets() // update section info curSection->fFileOffset = fileOffset; curSection->setBaseAddress(address); - + // keep track of trailing zero fill sections if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) firstZeroFillSection = curSection; @@ -3935,6 +5755,10 @@ void Writer::assignFileOffsets() 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; @@ -3953,8 +5777,13 @@ void Writer::assignFileOffsets() // page align segment size curSegment->fFileSize = (curSegment->fFileSize+4095) & (-4096); curSegment->fSize = (curSegment->fSize+4095) & (-4096); - if ( curSegment->fBaseAddress == nextContiguousAddress ) + if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) { nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096); + if ( curSegment->fInitProtection & VM_PROT_WRITE ) + nextWritableAddress = nextContiguousAddress; + else + nextReadOnlyAddress = nextContiguousAddress; + } } } @@ -4009,7 +5838,7 @@ void Writer::adjustLinkEditSections() while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) ++firstLinkEditSectionIndex; - const unsigned int sectionCount = lastSeg->fSections.size(); + const unsigned int linkEditSectionCount = lastSeg->fSections.size(); uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); if ( fPadSegmentInfo != NULL ) { @@ -4027,13 +5856,19 @@ void Writer::adjustLinkEditSections() address = fOptions.zeroPageSize(); lastSeg->fBaseAddress = fOptions.zeroPageSize(); } - for (unsigned int i=firstLinkEditSectionIndex; i < sectionCount; ++i) { + for (unsigned int i=firstLinkEditSectionIndex; i < linkEditSectionCount; ++i) { std::vector& atoms = lastSeg->fSections[i]->fAtoms; - const unsigned int atomCount = atoms.size(); - uint64_t sectionOffset = 0; - lastSeg->fSections[i]->fFileOffset = fileOffset; + // 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); - for (unsigned int j=0; j < atomCount; ++j) { + 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) ); @@ -4082,8 +5917,9 @@ ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom::getSymbolTableInclusio { switch ( fWriter.fOptions.outputKind() ) { case Options::kDynamicExecutable: - case Options::kStaticExecutable: return ObjectFile::Atom::kSymbolTableInAndNeverStrip; + case Options::kStaticExecutable: + return ObjectFile::Atom::kSymbolTableInAsAbsolute; case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDyld: @@ -4156,7 +5992,7 @@ void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const // get flags uint32_t flags = 0; if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { - if ( ! fWriter.fSeenFollowOnReferences ) + if ( fWriter.fCanScatter ) flags = MH_SUBSECTIONS_VIA_SYMBOLS; } else { @@ -4181,9 +6017,21 @@ void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const 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 @@ -4191,16 +6039,15 @@ void MachHeaderAtom::copyRawContent(uint8_t buffer[]) const uint32_t commandsCount = 0; std::vector& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; - const unsigned int atomCount = loadCommandAtoms.size(); - for (unsigned int i=0; i < atomCount; ++i) { - ObjectFile::Atom* atom = loadCommandAtoms[i]; + 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) + else if ( atom->getSize() != 0 ) ++commandsCount; } @@ -4218,7 +6065,20 @@ void MachHeaderAtom::setHeaderInfo(macho_header& header) const { header.set_magic(MH_MAGIC); header.set_cputype(CPU_TYPE_POWERPC); - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); + switch ( fWriter.fCpuConstraint ) { + case ObjectFile::Reader::kCpuAny: + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); + break; + case ObjectFile::Reader::kCpuG3: + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_750); + break; + case ObjectFile::Reader::kCpuG4: + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_7400); + break; + case ObjectFile::Reader::kCpuG5: + header.set_cpusubtype(CPU_SUBTYPE_POWERPC_970); + break; + } } template <> @@ -4226,7 +6086,10 @@ void MachHeaderAtom::setHeaderInfo(macho_header& header) const { header.set_magic(MH_MAGIC_64); header.set_cputype(CPU_TYPE_POWERPC64); - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); + 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); } @@ -4243,7 +6106,11 @@ void MachHeaderAtom::setHeaderInfo(macho_header& header) cons { header.set_magic(MH_MAGIC_64); header.set_cputype(CPU_TYPE_X86_64); - header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL); + 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); } @@ -4406,7 +6273,7 @@ void SegmentLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const 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); + 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); @@ -4432,6 +6299,12 @@ 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 ( (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); } @@ -4450,16 +6323,37 @@ SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer) { 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 ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) - return this->alignedSize(sizeof(macho_symtab_command

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

) + sizeof(macho_dysymtab_command

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

)); } template @@ -4476,7 +6370,7 @@ void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); // build LC_DYSYMTAB command - if ( fWriter.fOptions.outputKind() != Options::kStaticExecutable ) { + 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); @@ -4487,6 +6381,14 @@ void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const 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 ) { @@ -4498,10 +6400,11 @@ void SymbolTableLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const } } + template unsigned int SymbolTableLoadCommandsAtom::commandCount() { - return (fWriter.fOptions.outputKind() == Options::kStaticExecutable) ? 1 : 2; + return fNeedsDynamicSymbolTable ? 2 : 1; } template @@ -4548,27 +6451,32 @@ void AllowableClientLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const template uint64_t DylibLoadCommandsAtom::getSize() const { - const char* path = fInfo.reader->getInstallPath(); - if ( fInfo.options.fInstallPathOverride != NULL ) - path = fInfo.options.fInstallPathOverride; - return this->alignedSize(sizeof(macho_dylib_command

) + strlen(path) + 1); + 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(); - if ( fInfo.options.fInstallPathOverride != NULL ) - path = fInfo.options.fInstallPathOverride; macho_dylib_command

* cmd = (macho_dylib_command

*)buffer; if ( fInfo.options.fWeakImport ) 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(fInfo.reader->getTimestamp()); + 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(); @@ -4586,16 +6494,13 @@ uint64_t DylibIDLoadCommandsAtom::getSize() const template void DylibIDLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { - struct timeval currentTime = { 0 , 0 }; - gettimeofday(¤tTime, NULL); - time_t timestamp = currentTime.tv_sec; 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(timestamp); + 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()); @@ -4632,6 +6537,30 @@ void SubUmbrellaLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const 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 { @@ -4645,6 +6574,7 @@ void UUIDLoadCommandAtom::copyRawContent(uint8_t buffer[]) const } } + template uint64_t SubLibraryLoadCommandsAtom::getSize() const { @@ -4752,7 +6682,7 @@ void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const cmd->set_count(16); // i386_THREAD_STATE_COUNT; cmd->set_thread_register(10, start); if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(15, fWriter.fOptions.customStackAddr()); // uesp + cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp } @@ -4773,6 +6703,25 @@ void ThreadsLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const } +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 LoadCommandsPaddingAtom::copyRawContent(uint8_t buffer[]) const { @@ -4843,6 +6792,7 @@ uint64_t ExternalRelocationsLinkEditAtom::getSize() const template void ExternalRelocationsLinkEditAtom::copyRawContent(uint8_t buffer[]) const { + std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter

()); memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); } @@ -4873,445 +6823,385 @@ void IndirectTableLinkEditAtom::copyRawContent(uint8_t buffer[]) const -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 -{ - return kBufferSize * fFullBuffers.size() + fCurrentBufferUsed; -} - -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); -} - -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 -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 <> -bool StubAtom::pic() const -{ - // no-pic stubs for ppc64 don't work if lazy pointer is above low 2GB. - // This usually only happens when a large zero-page is requested - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - return (fWriter.fPageZeroAtom->getSize() > 4096); - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - return true; - case Options::kObjectFile: - case Options::kDyld: - case Options::kStaticExecutable: - break; - } - throw "internal ld64 error: file type does not use stubs"; -} - -template <> -bool StubAtom::pic() const -{ - return ( fWriter.fOptions.outputKind() != Options::kDynamicExecutable ); -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -ObjectFile::Alignment StubAtom::getAlignment() const -{ - return 2; -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp = new LazyPointerAtom(writer, target); - if ( pic() ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference(12, ppc::kPICBaseHigh16, lp, 0, NULL, 8)); - fReferences.push_back(new WriterReference(20, ppc::kPICBaseLow16, lp, 0, NULL, 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) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp = new LazyPointerAtom(writer, target); - if ( pic() ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference(12, ppc64::kPICBaseHigh16, lp, 0, NULL, 8)); - fReferences.push_back(new WriterReference(20, ppc64::kPICBaseLow14, lp, 0, NULL, 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) - : WriterAtom(writer, Segment::fgImportSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); -} - -template <> -StubAtom::StubAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom* lp = new LazyPointerAtom(writer, target); - fReferences.push_back(new WriterReference(2, x86_64::kPCRel32, lp)); -} - -template -const char* StubAtom::stubName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$stub", name); - return buf; -} - -template <> -uint64_t StubAtom::getSize() const +template +uint64_t ModuleInfoLinkEditAtom::getSize() const { - return ( pic() ? 32 : 16 ); + return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

) + + sizeof(macho_dylib_module

) + + this->getReferencesCount()*sizeof(uint32_t); } -template <> -uint64_t StubAtom::getSize() const +template +uint32_t ModuleInfoLinkEditAtom::getTableOfContentsFileOffset() const { - return ( pic() ? 32 : 16 ); + return this->getFileOffset(); } -template <> -uint64_t StubAtom::getSize() const +template +uint32_t ModuleInfoLinkEditAtom::getModuleTableFileOffset() const { - return 5; + return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents

); } -template <> -uint64_t StubAtom::getSize() const +template +uint32_t ModuleInfoLinkEditAtom::getReferencesFileOffset() const { - return 6; + return this->getModuleTableFileOffset() + sizeof(macho_dylib_module

); } -template <> -ObjectFile::Alignment StubAtom::getAlignment() const +template +uint32_t ModuleInfoLinkEditAtom::getReferencesCount() const { - // special case x86 fast stubs to be byte aligned - return 0; + return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount; } -template <> -void StubAtom::copyRawContent(uint8_t buffer[]) const +template +void ModuleInfoLinkEditAtom::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 + 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); } - 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 + // 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); + } + } } -} - -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 + 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); } - 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 + 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 <> -void StubAtom::copyRawContent(uint8_t buffer[]) const + + +template +StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) + : LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0) { - buffer[0] = 0xF4; - buffer[1] = 0xF4; - buffer[2] = 0xF4; - buffer[3] = 0xF4; - buffer[4] = 0xF4; + 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 <> -void StubAtom::copyRawContent(uint8_t buffer[]) const +template +uint64_t StringsLinkEditAtom::getSize() const { - buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; + // align size + return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t)); } -// x86_64 stubs are 7 bytes and need no alignment -template <> -ObjectFile::Alignment StubAtom::getAlignment() const +template +void StringsLinkEditAtom::copyRawContent(uint8_t buffer[]) const { - return 0; + 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 <> -const char* StubAtom::getSectionName() const +template +int32_t StringsLinkEditAtom::add(const char* name) { - return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); + 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 <> -const char* StubAtom::getSectionName() const + +template +int32_t StringsLinkEditAtom::addUnique(const char* name) { - return ( pic() ? "__picsymbolstub1" : "__symbol_stub1"); + 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* StubAtom::getSectionName() const + +template +const char* StringsLinkEditAtom::stringForIndex(int32_t index) const { - return "__jump_table"; + 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 <> -StubHelperAtom::StubHelperAtom(Writer& writer, ObjectFile::Atom& target, ObjectFile::Atom& lazyPointer) - : WriterAtom(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) +template +BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int islandRegion, ObjectFile::Atom& target, uint32_t targetOffset) + : WriterAtom(writer, Segment::fgTextSegment), fTarget(target), fTargetOffset(targetOffset) { - writer.fAllSynthesizedStubHelpers.push_back(this); - - fReferences.push_back(new WriterReference(3, x86_64::kPCRel32, &lazyPointer)); - fReferences.push_back(new WriterReference(8, x86_64::kPCRel32, writer.fDyldHelper)); - if ( writer.fDyldHelper == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; + 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 <> -uint64_t StubHelperAtom::getSize() const +void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const { - return 12; + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); } template <> -void StubHelperAtom::copyRawContent(uint8_t buffer[]) const +void BranchIslandAtom::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; + int64_t displacement = fTarget.getAddress() + fTargetOffset - this->getAddress(); + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); } -template -const char* StubHelperAtom::stubName(const char* name) +template <> +uint64_t BranchIslandAtom::getSize() const { - char* buf; - asprintf(&buf, "%s$stubHelper", name); - return buf; + return 4; } - -// specialize lazy pointer for x86_64 to initially pointer to stub helper template <> -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target) +uint64_t BranchIslandAtom::getSize() const { - writer.fAllSynthesizedLazyPointers.push_back(this); - - StubHelperAtom* helper = new StubHelperAtom(writer, target, *this); - fReferences.push_back(new WriterReference(0, x86_64::kPointer, helper)); + return 4; } + template -LazyPointerAtom::LazyPointerAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target) +uint64_t SegmentSplitInfoLoadCommandsAtom::getSize() const { - writer.fAllSynthesizedLazyPointers.push_back(this); - - fReferences.push_back(new WriterReference(0, A::kPointer, &target)); + 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 -const char* LazyPointerAtom::lazyPointerName(const char* name) +uint64_t SegmentSplitInfoContentAtom::getSize() const { - char* buf; - asprintf(&buf, "%s$lazy_pointer", name); - return buf; + return fEncodedData.size(); } template -void LazyPointerAtom::copyRawContent(uint8_t buffer[]) const +void SegmentSplitInfoContentAtom::copyRawContent(uint8_t buffer[]) const { - bzero(buffer, getSize()); + memcpy(buffer, &fEncodedData[0], fEncodedData.size()); } template -NonLazyPointerAtom::NonLazyPointerAtom(Writer& writer, ObjectFile::Atom& target) - : WriterAtom(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(target) +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() { - writer.fAllSynthesizedNonLazyPointers.push_back(this); + 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); - fReferences.push_back(new WriterReference(0, A::kPointer, &target)); + // add zeros to end to align size + while ( (fEncodedData.size() % sizeof(pint_t)) != 0 ) + fEncodedData.push_back(0); + } } + template -const char* NonLazyPointerAtom::nonlazyPointerName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$non_lazy_pointer", name); - return buf; +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 NonLazyPointerAtom::copyRawContent(uint8_t buffer[]) const +void ObjCInfoAtom::copyRawContent(uint8_t buffer[]) const { - bzero(buffer, getSize()); + 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 <> 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; } + + }; // namespace executable }; // namespace mach_o diff --git a/src/ObjectDump.cpp b/src/ObjectDump.cpp index accdf55..ce9801a 100644 --- a/src/ObjectDump.cpp +++ b/src/ObjectDump.cpp @@ -37,6 +37,10 @@ static bool sDumpContent= true; static bool sDumpStabs = false; static bool sSort = true; static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64; +static const char* sMatchName; +static int sPrintRestrict; +static int sPrintAlign; +static int sPrintName; __attribute__((noreturn)) @@ -152,13 +156,18 @@ static void dumpStabs(std::vector* stabs) static void dumpAtom(ObjectFile::Atom* atom) { + if(sMatchName && strcmp(sMatchName, atom->getDisplayName())) + return; + //printf("atom: %p\n", atom); // name - printf("name: %s\n", atom->getDisplayName()); + if(!sPrintRestrict || sPrintName) + printf("name: %s\n", atom->getDisplayName()); // scope - switch ( atom->getScope() ) { + if(!sPrintRestrict) + switch ( atom->getScope() ) { case ObjectFile::Atom::scopeTranslationUnit: printf("scope: translation unit\n"); break; @@ -170,10 +179,11 @@ static void dumpAtom(ObjectFile::Atom* atom) break; default: printf("scope: unknown\n"); - } + } // kind - switch ( atom->getDefinitionKind() ) { + if(!sPrintRestrict) + switch ( atom->getDefinitionKind() ) { case ObjectFile::Atom::kRegularDefinition: printf("kind: regular\n"); break; @@ -189,36 +199,51 @@ static void dumpAtom(ObjectFile::Atom* atom) case ObjectFile::Atom::kExternalWeakDefinition: printf("kind: weak import\n"); break; + case ObjectFile::Atom::kAbsoluteSymbol: + printf("kind: absolute symbol\n"); + break; default: - printf("scope: unknown\n"); - } + printf("kind: unknown\n"); + } // segment and section - printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + if(!sPrintRestrict) + printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); // attributes - printf("attrs: "); - if ( atom->dontDeadStrip() ) - printf("dont-dead-strip "); - if ( atom->isZeroFill() ) - printf("zero-fill "); - printf("\n"); + if(!sPrintRestrict) { + printf("attrs: "); + if ( atom->dontDeadStrip() ) + printf("dont-dead-strip "); + if ( atom->isZeroFill() ) + printf("zero-fill "); + printf("\n"); + } // size - printf("size: 0x%012llX\n", atom->getSize()); + if(!sPrintRestrict) + printf("size: 0x%012llX\n", atom->getSize()); // alignment - printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); + if(!sPrintRestrict || sPrintAlign) + printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); // content - if ( sDumpContent ) { + 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("\"%s\"", content); + 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) @@ -229,30 +254,38 @@ static void dumpAtom(ObjectFile::Atom* atom) } // references - 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()); + 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 - 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) { + 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()(ObjectFile::Atom* left, ObjectFile::Atom* right) + bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) { - return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0); + if ( left == right ) + return false; + return (strcmp(left->getDisplayName(), right->getDisplayName()) < 0); } }; @@ -274,10 +307,8 @@ static void dumpFile(ObjectFile::Reader* reader) if ( sSort ) std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter()); - for(std::vector::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) { + for(std::vector::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) dumpAtom(*it); - printf("\n"); - } } @@ -296,7 +327,6 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read 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) { - fprintf(stderr, "archs[i].cputype = %X\n", archs[i].cputype); if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { p = p + OSSwapBigToHostInt32(archs[i].offset); mh = (struct mach_header*)p; @@ -304,19 +334,37 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read } } if ( mach_o::relocatable::Reader::validFile(p) ) - return mach_o::relocatable::Reader::make(p, path, 0, options); + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); else if ( mach_o::relocatable::Reader::validFile(p) ) - return mach_o::relocatable::Reader::make(p, path, 0, options); + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); else if ( mach_o::relocatable::Reader::validFile(p) ) - return mach_o::relocatable::Reader::make(p, path, 0, options); + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); else if ( mach_o::relocatable::Reader::validFile(p) ) - return mach_o::relocatable::Reader::make(p, path, 0, options); + return new mach_o::relocatable::Reader::Reader(p, path, 0, options, 0); 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) { @@ -332,7 +380,7 @@ int main(int argc, const char* argv[]) sSort = false; } else if ( strcmp(arg, "-arch") == 0 ) { - const char* arch = argv[++i]; + const char* arch = ++i fAliases; }; @@ -90,6 +109,16 @@ public: uint32_t value; const char* string; }; + enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC }; + enum CpuConstraint { kCpuAny, kCpuG3, kCpuG4, kCpuG5 }; + + class DylibHander + { + public: + virtual ~DylibHander() {} + virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0; + }; + static Reader* createReader(const char* path, const ReaderOptions& options); @@ -99,24 +128,31 @@ public: virtual std::vector& getAtoms() = 0; virtual std::vector* getJustInTimeAtomsFor(const char* name) = 0; virtual std::vector* getStabs() = 0; - unsigned int getSortOrder() const { return fSortOrder; } - void setSortOrder(unsigned int order) { fSortOrder=order; } + virtual ObjcConstraint getObjCConstraint() { return kObjcNone; } + virtual CpuConstraint getCpuConstraint() { return kCpuAny; } + virtual bool objcReplacementClasses() { return false; } + + // For relocatable object files only + virtual bool canScatterAtoms() { return true; } + virtual void optimize(std::vector&, std::vector&, uint32_t) { } // 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 std::vector* getDependentLibraryPaths() { return NULL; } - virtual bool reExports(Reader*) { return false; } + 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; } + protected: - Reader() : fSortOrder(0) {} + Reader() {} virtual ~Reader() {} - - unsigned int fSortOrder; }; class Segment @@ -157,7 +193,7 @@ protected: struct Alignment { Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} - uint8_t leadingZeros() const { return (modulus==0) ? powerOf2 : __builtin_clz(modulus); } + uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } uint16_t powerOf2; uint16_t modulus; }; @@ -195,11 +231,16 @@ struct Alignment // 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 }; + enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; virtual Reader* getFile() const = 0; @@ -216,8 +257,8 @@ public: virtual bool mustRemainInSection() const = 0; virtual const char* getSectionName() const = 0; virtual Segment& getSegment() const = 0; - virtual bool requiresFollowOnAtom() 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; @@ -225,43 +266,21 @@ public: uint64_t getSectionOffset() const { return fSectionOffset; } - uint64_t getSegmentOffset() const { return fSegmentOffset; } uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } - unsigned int getSortOrder() const { return fSortOrder; } class Section* getSection() const { return fSection; } - void setSegmentOffset(uint64_t offset) { fSegmentOffset = offset; } - void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } - void setSection(class Section* sect) { fSection = sect; } - unsigned int setSortOrder(unsigned int order); // recursively sets follow-on atoms + virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } + virtual void setSection(class Section* sect) { fSection = sect; } protected: - Atom() : fSegmentOffset(0), fSectionOffset(0), fSortOrder(0), fSection(NULL) {} + Atom() : fSectionOffset(0), fSection(NULL) {} virtual ~Atom() {} - uint64_t fSegmentOffset; uint64_t fSectionOffset; - unsigned int fSortOrder; class Section* fSection; }; - -// recursively sets follow-on atoms -inline unsigned int Atom::setSortOrder(unsigned int order) -{ - if ( this->requiresFollowOnAtom() ) { - fSortOrder = order; - return this->getFollowOnAtom().setSortOrder(order+1); - } - else { - fSortOrder = order; - return (order + 1); - } -} - - - // // 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 @@ -287,15 +306,15 @@ inline unsigned int Atom::setSortOrder(unsigned int order) class Reference { public: + enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind }; - virtual bool isTargetUnbound() const = 0; - virtual bool isFromTargetUnbound() const = 0; + 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 bool hasFromTarget() const = 0; virtual Atom& getFromTarget() const = 0; virtual const char* getFromTargetName() const = 0; virtual uint64_t getFromTargetOffset() const = 0; diff --git a/src/SectCreate.cpp b/src/OpaqueSection.hpp similarity index 51% rename from src/SectCreate.cpp rename to src/OpaqueSection.hpp index 5677c32..ccb3f2f 100644 --- a/src/SectCreate.cpp +++ b/src/OpaqueSection.hpp @@ -22,11 +22,15 @@ * @APPLE_LICENSE_HEADER_END@ */ +#ifndef __OPAQUE_SECTION__ +#define __OPAQUE_SECTION__ + + #include #include "ObjectFile.h" -namespace SectCreate { +namespace opaque_section { class Segment : public ObjectFile::Segment @@ -36,7 +40,7 @@ public: 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; } + virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); } private: const char* fName; }; @@ -45,9 +49,13 @@ private: class Reader : public ObjectFile::Reader { public: - Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); + 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; } @@ -60,25 +68,58 @@ private: 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 NULL; } + virtual const char* getName() const { return fName; } virtual const char* getDisplayName() const; - virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } + 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 uint64_t getSize() const { return fFileLength; } - virtual std::vector& getReferences() const { return fgEmptyReferenceList; } + 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 bool requiresFollowOnAtom() const{ return false; } 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; @@ -88,39 +129,54 @@ public: protected: friend class Reader; - Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength) - : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fFileLength(fileLength) { } + 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; - - static std::vector fgEmptyReferenceList; + std::vector fReferences; }; -std::vector Atom::fgEmptyReferenceList; - + +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) +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)), sectionName, fileContent, fileLength)); + 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, "-sectcreate %s %s", fSegment.getName(), fSectionName); + sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName); return name; } @@ -130,18 +186,13 @@ void Atom::copyRawContent(uint8_t buffer[]) const memcpy(buffer, fFileContent, fFileLength); } -Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength) -{ - return new Reader(segmentName, sectionName, path, fileContent, fileLength); -} - }; - +#endif // __OPAQUE_SECTION__ diff --git a/src/Options.cpp b/src/Options.cpp index 1eb135b..50bd636 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -44,22 +45,27 @@ void throwf(const char* format, ...) } Options::Options(int argc, const char* argv[]) - : fOutputFile("a.out"), fArchitecture(0), fOutputKind(kDynamicExecutable), fBindAtLoad(false), - fStripLocalSymbols(false), fKeepPrivateExterns(false), - fInterposable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), fDeadStrip(kDeadStripOff), - fVersionMin(kMinUnset),fNameSpace(kTwoLevelNameSpace), - fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fEntryName("start"), fBaseAddress(0), + : fOutputFile("a.out"), fArchitecture(0), fOutputKind(kDynamicExecutable), fPrebind(false), fBindAtLoad(false), + fKeepPrivateExterns(false), + fInterposable(false), fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fForceSubtypeAll(false), + 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), fPICTreatment(kError), - fWeakReferenceMismatchTreatment(kWeakReferenceMismatchError), fMultiplyDefinedDynamic(kWarning), - fMultiplyDefinedUnused(kSuppress), fWarnOnMultiplyDefined(false), fClientName(NULL), - fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), - fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), fMinimumHeaderPad(0), - fCommonsMode(kCommonsIgnoreDylibs), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), - fEmitUUID(true),fWarnStabs(false), + 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), - fMakeTentativeDefinitionsReal(false) + fSharedRegionEligible(false), fPrintOrderFileStatistics(false), + fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), + fDeadStripDylibs(false), fSuppressWarnings(false), fSaveTempFiles(false) { + this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); this->parse(argc, argv); this->parsePostCommandLineEnvironmentSettings(); @@ -96,11 +102,6 @@ Options::OutputKind Options::outputKind() return fOutputKind; } -bool Options::stripLocalSymbols() -{ - return fStripLocalSymbols; -} - bool Options::bindAtLoad() { return fBindAtLoad; @@ -125,6 +126,8 @@ const char* Options::installPath() { if ( fDylibInstallName != NULL ) return fDylibInstallName; + else if ( fFinalName != NULL ) + return fFinalName; else return fOutputFile; } @@ -159,6 +162,11 @@ bool Options::interposable() return fInterposable; } +bool Options::needsModuleTable() +{ + return fNeedsModuleTable; +} + bool Options::ignoreOtherArchInputFiles() { return fIgnoreOtherArchFiles; @@ -184,9 +192,9 @@ Options::UndefinedTreatment Options::undefinedTreatment() return fUndefinedTreatment; } -Options::VersionMin Options::macosxVersionMin() +ObjectFile::ReaderOptions::VersionMin Options::macosxVersionMin() { - return fVersionMin; + return fReaderOptions.fVersionMin; } Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() @@ -194,21 +202,6 @@ Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment( return fWeakReferenceMismatchTreatment; } -Options::Treatment Options::multipleDefinitionsInDylibs() -{ - return fMultiplyDefinedDynamic; -} - -Options::Treatment Options::overridingDefinitionInDependentDylib() -{ - return fMultiplyDefinedUnused; -} - -bool Options::warnOnMultipleDefinitionsInObjectFiles() -{ - return fWarnOnMultiplyDefined; -} - const char* Options::umbrellaName() { return fUmbrellaName; @@ -259,10 +252,6 @@ bool Options::printWhyLive(const char* symbolName) return ( fWhyLive.find(symbolName) != fWhyLive.end() ); } -std::vector& Options::traceSymbols() -{ - return fTraceSymbols; -} const char* Options::initFunctionName() { @@ -282,7 +271,7 @@ bool Options::hasExportRestrictList() bool Options::allGlobalsAreDeadStripRoots() { // -exported_symbols_list means globals are not exported by default - if ( fExportMode == kExportSome ) + if ( fExportMode == kExportSome ) return false; // switch ( fOutputKind ) { @@ -329,11 +318,6 @@ bool Options::keepRelocations() return fKeepRelocations; } -bool Options::emitUUID() -{ - return fEmitUUID; -} - bool Options::warnStabs() { return fWarnStabs; @@ -353,15 +337,30 @@ bool Options::shouldExport(const char* symbolName) { switch (fExportMode) { case kExportSome: - return ( fExportSymbols.find(symbolName) != fExportSymbols.end() ); + return fExportSymbols.contains(symbolName); case kDontExportSome: - return ( fDontExportSymbols.find(symbolName) == fDontExportSymbols.end() ); + 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 ) @@ -429,6 +428,13 @@ Options::FileInfo Options::findLibrary(const char* rootName) 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 for (std::vector::iterator it = fLibrarySearchPaths.begin(); @@ -448,6 +454,8 @@ Options::FileInfo Options::findLibrary(const char* rootName) 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 ( checkForFile("%s/lib%s.a", dir, rootName, result) ) return result; } @@ -460,7 +468,7 @@ Options::FileInfo Options::findLibrary(const char* rootName) Options::FileInfo Options::findFramework(const char* frameworkName) { if ( frameworkName == NULL ) - throw "-frameowrk missing next argument"; + throw "-framework missing next argument"; char temp[strlen(frameworkName)+1]; strcpy(temp, frameworkName); const char* name = temp; @@ -509,7 +517,7 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi } } // try without suffix - if ( suffix != NULL ) + if ( suffix != NULL ) return findFramework(rootName, NULL); else throwf("framework not found %s", rootName); @@ -569,6 +577,114 @@ Options::FileInfo Options::findFile(const char* path) 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 { + 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 ) { + fprintf(stderr, "ld64: warning, -seg_addr_table file cannot be read: %s\n", 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) { @@ -592,7 +708,7 @@ void Options::loadFileList(const char* fileOfPaths) } char path[PATH_MAX]; - while ( fgets(path, 1024, file) != NULL ) { + while ( fgets(path, PATH_MAX, file) != NULL ) { path[PATH_MAX-1] = '\0'; char* eol = strchr(path, '\n'); if ( eol != NULL ) @@ -611,8 +727,97 @@ void Options::loadFileList(const char* fileOfPaths) 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, NameSet& set) +void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set) { // read in whole file int fd = ::open(fileOfExports, O_RDONLY, 0); @@ -645,7 +850,7 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, Name } break; case inSymbol: - if ( *s == '\n' ) { + if ( (*s == '\n') || (*s == '\r') ) { *s = '\0'; // removing any trailing spaces char* last = s-1; @@ -659,7 +864,7 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, Name } break; case inComment: - if ( *s == '\n' ) + if ( (*s == '\n') || (*s == '\r') ) state = lineStart; break; } @@ -682,6 +887,97 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, Name // 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' ) { + fprintf(stderr, "ld64 warning: line needs two symbols but has only one at line #%d in \"%s\"\n", lineNumber, fileOfAliases); + ++lineNumber; + state = lineStart; + } + else if ( isspace(*s) ) { + *s = '\0'; + state = inBetween; + } + break; + case inBetween: + if ( *s == '\n' ) { + fprintf(stderr, "ld64 warning: line needs two symbols but has only one at line #%d in \"%s\"\n", 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 ) @@ -724,19 +1020,19 @@ void Options::setVersionMin(const char* version) switch ( num ) { case 0: case 1: - fVersionMin = k10_1; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_1; break; case 2: - fVersionMin = k10_2; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_2; break; case 3: - fVersionMin = k10_3; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_3; break; case 4: - fVersionMin = k10_4; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; break; default: - fVersionMin = k10_5; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_5; break; } } @@ -775,10 +1071,21 @@ Options::CommonsMode Options::parseCommonsTreatment(const char* mode) throw "invalid option to -commons [ ignore_dylibs | use_dylibs | error ]"; } -void Options::setDylibInstallNameOverride(const char* paths) +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) @@ -788,6 +1095,31 @@ uint64_t Options::parseAddress(const char* addr) 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 // @@ -811,9 +1143,198 @@ uint32_t Options::parseVersionNumber(const char* 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; + } + 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) { - fprintf(stderr, "ld64: warning -sectorder not yet supported for 64-bit code\n"); + if ( (strcmp(section, "__cstring") == 0) && (strcmp(segment, "__TEXT") == 0) ) { + parseOrderFile(path, true); + } + else if ( (strncmp(section, "__literal",9) == 0) && (strcmp(segment, "__TEXT") == 0) ) { + fprintf(stderr, "ld64 warning: sorting of __literal[4,8,16] sections not supported\n"); + } + 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) @@ -863,12 +1384,11 @@ void Options::addSectionAlignment(const char* segment, const char* section, cons fprintf(stderr, "ld64 warning: zero is not a valid -sectalign\n"); value = 1; } - + // alignment is power of 2 (e.g. page alignment = 12) - uint8_t alignment = (uint8_t)log2(value); - + uint8_t alignment = (uint8_t)__builtin_ctz(value); if ( (unsigned long)(1 << alignment) != value ) { - fprintf(stderr, "ld64 warning: alignment for -sectalign %s %s is not a power of two, using 0x%X\n", + fprintf(stderr, "ld64 warning: alignment for -sectalign %s %s is not a power of two, using 0x%X\n", segment, section, 1 << alignment); } @@ -876,6 +1396,30 @@ void Options::addSectionAlignment(const char* segment, const char* section, cons 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) +{ + if ( emitWarnings() ) + fprintf(stderr, "ld64: warning: option %s is obsolete and being ignored\n", arg); +} + + + + // // Process all command line arguments. // @@ -908,6 +1452,13 @@ void Options::parse(int argc, const char* argv[]) 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]); } @@ -915,7 +1466,9 @@ void Options::parse(int argc, const char* argv[]) // default } else if ( strcmp(arg, "-static") == 0 ) { - fOutputKind = kStaticExecutable; + if ( fOutputKind != kObjectFile ) + fOutputKind = kStaticExecutable; + fReaderOptions.fForStatic = true; } else if ( strcmp(arg, "-dylib") == 0 ) { fOutputKind = kDynamicLibrary; @@ -937,14 +1490,14 @@ void Options::parse(int argc, const char* argv[]) fOutputFile = argv[++i]; } else if ( arg[1] == 'l' ) { - fInputFiles.push_back(findLibrary(&arg[2])); + 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; - fInputFiles.push_back(info); + addLibrary(info); } // Avoid lazy binding. // ??? Deprecate. @@ -967,49 +1520,78 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-all_load") == 0 ) { fReaderOptions.fFullyLoadArchives = true; } - // Similar to --whole-archive, but for all ObjC classes. + else if ( strcmp(arg, "-noall_load") == 0) { + warnObsolete(arg); + } + // Similar to -all_load else if ( strcmp(arg, "-ObjC") == 0 ) { - fReaderOptions.fLoadObjcClassesInArchives = true; + fReaderOptions.fLoadAllObjcObjectsFromArchives = true; } // Library versioning. - else if ( strcmp(arg, "-dylib_compatibility_version") == 0 ) { - fDylibCompatVersion = parseVersionNumber(argv[++i]); - } - else if ( strcmp(arg, "-dylib_current_version") == 0 ) { - fDylibCurrentVersion = parseVersionNumber(argv[++i]); + 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) ) { + 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) ) { - fBaseAddress = parseAddress(argv[++i]); + const char* address = argv[++i]; + if ( address == NULL ) + throwf("%s missing
", arg); + fBaseAddress = parseAddress(address); } else if ( strcmp(arg, "-e") == 0 ) { fEntryName = argv[++i]; } // Same as -@ from the FSF linker. else if ( strcmp(arg, "-filelist") == 0 ) { - loadFileList(argv[++i]); + 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; } - // ??? Deprecate else if ( strcmp(arg, "-final_output") == 0 ) { - ++i; - // ignore for now + 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. @@ -1028,10 +1610,34 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-unexported_symbols_list") == 0 ) { if ( fExportMode == kExportSome ) - throw "can't use -exported_symbols_list and -unexported_symbols_list"; + 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; @@ -1043,19 +1649,18 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-weak_library") == 0 ) { FileInfo info = findFile(argv[++i]); info.options.fWeakImport = true; - fInputFiles.push_back(info); + addLibrary(info); } else if ( strcmp(arg, "-framework") == 0 ) { - fInputFiles.push_back(findFramework(argv[++i])); + addLibrary(findFramework(argv[++i])); } else if ( strcmp(arg, "-weak_framework") == 0 ) { FileInfo info = findFramework(argv[++i]); info.options.fWeakImport = true; - fInputFiles.push_back(info); + addLibrary(info); } - // ??? Deprecate when we get -Bstatic/-Bdynamic. else if ( strcmp(arg, "-search_paths_first") == 0 ) { - fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; + // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-undefined") == 0 ) { setUndefinedTreatment(argv[++i]); @@ -1076,16 +1681,9 @@ void Options::parse(int argc, const char* argv[]) else if ( temp == kInvalid ) throw "invalid option to -read_only_relocs [ warning | error | suppress ]"; } - // Specifies whether or not there are intra section - // relocations and what to do when found. Could be - // errors, warnings, or suppressed. else if ( strcmp(arg, "-sect_diff_relocs") == 0 ) { - fPICTreatment = parseTreatment(argv[++i]); - - if ( fPICTreatment == kNULL ) - throw "-sect_diff_relocs missing [ warning | error | suppress ]"; - else if ( fPICTreatment == kInvalid ) - throw "invalid option to -sect_diff_relocs [ warning | error | suppress ]"; + warnObsolete(arg); + ++i; } // Warn, error or make strong a mismatch between weak // and non-weak references. @@ -1100,98 +1698,129 @@ void Options::parse(int argc, const char* argv[]) fPrebind = true; } else if ( strcmp(arg, "-noprebind") == 0 ) { + warnObsolete(arg); fPrebind = false; } - // ??? Deprecate else if ( strcmp(arg, "-prebind_allow_overlap") == 0 ) { - // Do not handle and suppress warnings always. + warnObsolete(arg); } - // ??? Deprecate else if ( strcmp(arg, "-prebind_all_twolevel_modules") == 0 ) { - // Ignore. + warnObsolete(arg); } - // ??? Deprecate else if ( strcmp(arg, "-noprebind_all_twolevel_modules") == 0 ) { - // Ignore. + warnObsolete(arg); } - // Sets a bit in the main executable only that causes fix_prebinding - // not to run. This is always set. else if ( strcmp(arg, "-nofixprebinding") == 0 ) { - // Ignore. + warnObsolete(arg); } // This should probably be deprecated when we respect -L and -F // when searching for libraries. else if ( strcmp(arg, "-dylib_file") == 0 ) { - setDylibInstallNameOverride(argv[++i]); + addDylibOverride(argv[++i]); } - // Allows us to rewrite full paths to be relocatable based on - // the path name of the executable. + // 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; + } + } } - // ??? Deprecate // Aligns all segments to the power of 2 boundary specified. else if ( strcmp(arg, "-segalign") == 0 ) { - // Ignore. + 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 ) { - // FIX FIX - i += 2; + 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) && emitWarnings() ) + fprintf(stderr, "ld64: warning, -segaddr %s not page aligned, rounding down\n", seg.name); + fCustomSegmentAddresses.push_back(seg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { - // Ignore. - ++i; + fBaseAddress = parseAddress(argv[++i]); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { - // Ignore. - ++i; + fBaseWritableAddress = parseAddress(argv[++i]); + fSplitSegs = true; } // ??? Deprecate when we get rid of basing at build time. else if ( strcmp(arg, "-seg_addr_table") == 0 ) { - // Ignore. - ++i; + const char* name = argv[++i]; + if ( name == NULL ) + throw "-seg_addr_table missing argument"; + fSegAddrTablePath = name; } - // ??? Deprecate. else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { - // Ignore. + warnObsolete(arg); ++i; } else if ( strcmp(arg, "-segprot") == 0 ) { - // FIX FIX - i += 3; + 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 ) { - fZeroPageSize = parseAddress(argv[++i]); + 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 ) + if ( (fZeroPageSize != temp) && emitWarnings() ) fprintf(stderr, "ld64: warning, -pagezero_size not page aligned, rounding down\n"); fZeroPageSize = temp; } else if ( strcmp(arg, "-stack_addr") == 0 ) { - fStackAddr = parseAddress(argv[++i]); + const char* address = argv[++i]; + if ( address == NULL ) + throw "-stack_addr missing
"; + fStackAddr = parseAddress(address); } else if ( strcmp(arg, "-stack_size") == 0 ) { - fStackSize = parseAddress(argv[++i]); + 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) && emitWarnings() ) + fprintf(stderr, "ld64: warning, -stack_size not page aligned, rounding down\n"); } 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 ) { - // FIX FIX + warnObsolete(arg); } else if ( strcmp(arg, "-sectobjectsymbols") == 0 ) { - // FIX FIX + warnObsolete(arg); i += 2; } else if ( strcmp(arg, "-bundle_loader") == 0 ) { @@ -1203,7 +1832,7 @@ void Options::parse(int argc, const char* argv[]) fInputFiles.push_back(info); } else if ( strcmp(arg, "-private_bundle") == 0 ) { - // FIX FIX + warnObsolete(arg); } else if ( strcmp(arg, "-twolevel_namespace_hints") == 0 ) { // FIX FIX @@ -1212,49 +1841,31 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-macosx_version_min") == 0 ) { setVersionMin(argv[++i]); } - // This option (unlike -m below) only affects how we warn - // on multiple definitions inside dynamic libraries. else if ( strcmp(arg, "-multiply_defined") == 0 ) { - fMultiplyDefinedDynamic = parseTreatment(argv[++i]); - - if ( fMultiplyDefinedDynamic == kNULL ) - throw "-multiply_defined missing [ warning | error | suppress ]"; - else if ( fMultiplyDefinedDynamic == kInvalid ) - throw "invalid option to -multiply_defined [ warning | error | suppress ]"; + //warnObsolete(arg); + ++i; } else if ( strcmp(arg, "-multiply_defined_unused") == 0 ) { - fMultiplyDefinedUnused = parseTreatment(argv[++i]); - - if ( fMultiplyDefinedUnused == kNULL ) - throw "-multiply_defined_unused missing [ warning | error | suppress ]"; - else if ( fMultiplyDefinedUnused == kInvalid ) - throw "invalid option to -multiply_defined_unused [ warning | error | suppress ]"; + warnObsolete(arg); + ++i; } else if ( strcmp(arg, "-nomultidefs") == 0 ) { - // FIX FIX + 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 ) { - const char* name = &arg[2]; - - if ( name == NULL ) - throw "-y missing argument"; - - fTraceSymbols.push_back(name); + warnObsolete("-y"); } // Same output as -y, but output number of undefined symbols only. else if ( strcmp(arg, "-Y") == 0 ) { - char* endptr; - fLimitUndefinedSymbols = strtoul (argv[++i], &endptr, 10); - - if(*endptr != '\0') - throw "invalid argument for -Y [decimal number]"; + //warnObsolete(arg); + ++i; } // This option affects all objects linked into the final result. else if ( strcmp(arg, "-m") == 0 ) { - fWarnOnMultiplyDefined = true; + warnObsolete(arg); } else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { fReaderOptions.fWhyLoad = true; @@ -1272,33 +1883,38 @@ void Options::parse(int argc, const char* argv[]) fInitialUndefines.push_back(name); } else if ( strcmp(arg, "-U") == 0 ) { - // FIX FIX - ++i; + const char* name = argv[++i]; + if ( name == NULL ) + throw "-U missing argument"; + fAllowedUndefined.insert(name); } else if ( strcmp(arg, "-s") == 0 ) { - fStripLocalSymbols = true; + warnObsolete(arg); + fLocalSymbolHandling = kLocalSymbolsNone; fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; } else if ( strcmp(arg, "-x") == 0 ) { - fStripLocalSymbols = true; + fLocalSymbolHandling = kLocalSymbolsNone; } else if ( strcmp(arg, "-S") == 0 ) { fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; } else if ( strcmp(arg, "-X") == 0 ) { - // FIX FIX + warnObsolete(arg); } else if ( strcmp(arg, "-Si") == 0 ) { + warnObsolete(arg); fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; } else if ( strcmp(arg, "-b") == 0 ) { - // FIX FIX + warnObsolete(arg); } else if ( strcmp(arg, "-Sn") == 0 ) { + warnObsolete(arg); fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; } else if ( strcmp(arg, "-Sp") == 0 ) { - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoMinimal; + warnObsolete(arg); } else if ( strcmp(arg, "-dead_strip") == 0 ) { fDeadStrip = kDeadStripOnPlusUnusedInits; @@ -1307,7 +1923,7 @@ void Options::parse(int argc, const char* argv[]) fDeadStrip = kDeadStripOn; } else if ( strcmp(arg, "-w") == 0 ) { - // FIX FIX + // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-arch_errors_fatal") == 0 ) { // FIX FIX @@ -1315,9 +1931,6 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-M") == 0 ) { // FIX FIX } - else if ( strcmp(arg, "-whatsloaded") == 0 ) { - // FIX FIX - } else if ( strcmp(arg, "-headerpad") == 0 ) { const char* size = argv[++i]; if ( size == NULL ) @@ -1325,13 +1938,16 @@ void Options::parse(int argc, const char* argv[]) fMinimumHeaderPad = parseAddress(size); } else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { - // FIX FIX + fMaxMinimumHeaderPad = true; } else if ( strcmp(arg, "-t") == 0 ) { - // FIX FIX + fReaderOptions.fLogAllFiles = true; + } + else if ( strcmp(arg, "-whatsloaded") == 0 ) { + fReaderOptions.fLogObjectFiles = true; } else if ( strcmp(arg, "-A") == 0 ) { - // FIX FIX + warnObsolete(arg); ++i; } else if ( strcmp(arg, "-umbrella") == 0 ) { @@ -1399,7 +2015,7 @@ void Options::parse(int argc, const char* argv[]) fStatistics = true; } else if ( strcmp(arg, "-d") == 0 ) { - fMakeTentativeDefinitionsReal = true; + fReaderOptions.fMakeTentativeDefinitionsReal = true; } else if ( strcmp(arg, "-v") == 0 ) { // previously handled by buildSearchPaths() @@ -1412,18 +2028,99 @@ void Options::parse(int argc, const char* argv[]) // previously handled by buildSearchPaths() } else if ( strcmp(arg, "-no_uuid") == 0 ) { - fEmitUUID = false; + 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 ) { - fprintf(stderr, "ld64: -i option (indirect symbols) not supported\n"); + 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, "-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]); + 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, "-new_linker") == 0 ) { + // ignore } else { throwf("unknown option: %s", arg); } } else { - fInputFiles.push_back(findFile(arg)); + FileInfo info = findFile(arg); + if ( strcmp(&info.path[strlen(info.path)-2], ".a") == 0 ) + addLibrary(info); + else + fInputFiles.push_back(info); } } } @@ -1456,8 +2153,8 @@ void Options::buildSearchPaths(int argc, const char* argv[]) addStandardLibraryDirectories = false; else if ( strcmp(argv[i], "-v") == 0 ) { fVerbose = true; - extern const char ld64VersionString[]; - fprintf(stderr, "%s", ld64VersionString); + extern const char ldVersionString[]; + fprintf(stderr, "%s", ldVersionString); // if only -v specified, exit cleanly if ( argc == 2 ) exit(0); @@ -1468,14 +2165,21 @@ void Options::buildSearchPaths(int argc, const char* argv[]) 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 ) { + fSuppressWarnings = true; + } } if ( addStandardLibraryDirectories ) { libraryPaths.push_back("/usr/lib"); libraryPaths.push_back("/usr/local/lib"); frameworkPaths.push_back("/Library/Frameworks/"); - frameworkPaths.push_back("/Network/Library/Frameworks/"); frameworkPaths.push_back("/System/Library/Frameworks/"); + frameworkPaths.push_back("/Network/Library/Frameworks/"); } // now merge sdk and library paths to make real search paths @@ -1578,6 +2282,9 @@ void Options::parsePreCommandLineEnvironmentSettings() if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives) fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE"); + + if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) + fPrintOrderFileStatistics = true; } // this is run after the command line is parsed @@ -1587,7 +2294,15 @@ void Options::parsePostCommandLineEnvironmentSettings() 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 ); + } } void Options::reconfigureDefaults() @@ -1597,53 +2312,114 @@ void Options::reconfigureDefaults() case Options::kObjectFile: fReaderOptions.fForFinalLinkedImage = false; break; - case Options::kDynamicExecutable: - case Options::kStaticExecutable: + case Options::kDyld: + fReaderOptions.fForDyld = true; + fReaderOptions.fForFinalLinkedImage = true; + break; case Options::kDynamicLibrary: case Options::kDynamicBundle: - case Options::kDyld: + fReaderOptions.fForFinalLinkedImage = true; + break; + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + fReaderOptions.fLinkingMainExecutable = true; fReaderOptions.fForFinalLinkedImage = true; break; } // set default min OS version - if ( fVersionMin == kMinUnset ) { - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - fVersionMin = k10_2; - break; - case CPU_TYPE_I386: - case CPU_TYPE_POWERPC64: - case CPU_TYPE_X86_64: - fVersionMin = k10_4; - default: - // architecture not specified - fVersionMin = k10_4; - break; - } + 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 ) + setVersionMin(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 ( fVersionMin < k10_4 ) { + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { //fprintf(stderr, "ld64 warning: -macosx_version_min should be 10.4 or later for i386\n"); - fVersionMin = k10_4; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; } break; case CPU_TYPE_POWERPC64: - if ( fVersionMin < k10_4 ) { + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { //fprintf(stderr, "ld64 warning: -macosx_version_min should be 10.4 or later for ppc64\n"); - fVersionMin = k10_4; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; } break; case CPU_TYPE_X86_64: - if ( fVersionMin < k10_4 ) { + if ( fReaderOptions.fVersionMin < ObjectFile::ReaderOptions::k10_4 ) { //fprintf(stderr, "ld64 warning: -macosx_version_min should be 10.4 or later for x86_64\n"); - fVersionMin = k10_4; + fReaderOptions.fVersionMin = ObjectFile::ReaderOptions::k10_4; } break; } + + // determine if info for shared region should be added + if ( fOutputKind == Options::kDynamicLibrary ) { + if ( fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + 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()); + + + // split segs only allowed for dylibs + if ( fSplitSegs ) { + if ( fOutputKind != Options::kDynamicLibrary ) + fSplitSegs = false; + // split seg only supported for ppc and i386 + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + 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 ) { + // only split seg dylibs are prebound + if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs ) + fPrebind = false; + } + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + fPrebind = false; + break; + } + } + + + // 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; + } + } + } @@ -1678,7 +2454,7 @@ void Options::checkIllegalOptionCombinations() break; } } - if ( ! found ) + if ( ! found && emitWarnings() ) fprintf(stderr, "ld64 warning: -sub_umbrella %s does not match a supplied dylib\n", subUmbrella); } @@ -1700,7 +2476,7 @@ void Options::checkIllegalOptionCombinations() break; } } - if ( ! found ) + if ( ! found && emitWarnings() ) fprintf(stderr, "ld64 warning: -sub_library %s does not match a supplied dylib\n", subLibrary); } @@ -1736,13 +2512,13 @@ void Options::checkIllegalOptionCombinations() if ( fStackAddr == 0 ) { fStackAddr = 0xC0000000; } - if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) ) + if ( (fStackAddr > 0xB0000000) && ((fStackAddr-fStackSize) < 0xB0000000) && emitWarnings() ) fprintf(stderr, "ld64 warning: custom stack placement overlaps and will disable shared region\n"); break; case CPU_TYPE_POWERPC64: case CPU_TYPE_X86_64: if ( fStackAddr == 0 ) { - fStackAddr = 0x0007FFFF00000000LL; + fStackAddr = 0x00007FFF5C000000LL; } break; } @@ -1776,10 +2552,20 @@ void Options::checkIllegalOptionCombinations() } } - // check -client_name is only used when -bundle is specified - if ( (fClientName != NULL) && (fOutputKind != Options::kDynamicBundle) ) - throw "-client_name can only be used with -bundle"; - + // 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"; @@ -1788,22 +2574,77 @@ void Options::checkIllegalOptionCombinations() 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 ( fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) + 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 - for (NameSet::iterator it=fExportSymbols.begin(); it != fExportSymbols.end(); it++) { + std::vector impliedExports; + for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) { const char* name = *it; // never export .eh symbols - if ( strcmp(&name[strlen(name)-3], ".eh") != 0 ) + const int len = strlen(name); + if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) + fprintf(stderr, "ld64 warning: ignoring %s in export list\n", 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: + 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) { @@ -1814,7 +2655,7 @@ void Options::checkIllegalOptionCombinations() break; case CPU_TYPE_POWERPC64: // first 4GB for ppc64 on 10.5 - if ( fVersionMin >= k10_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 @@ -1838,16 +2679,114 @@ void Options::checkIllegalOptionCombinations() case Options::kDynamicBundle: case Options::kObjectFile: case Options::kDyld: - throw "-pagezero_size option can only be used when linking a main executable"; - } + 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\n"; + // 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\n"; + 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: +// 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 ) + 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/src/Options.h b/src/Options.h index ad2e45b..3670756 100644 --- a/src/Options.h +++ b/src/Options.h @@ -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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -39,12 +39,11 @@ void throwf (const char* format, ...) __attribute__ ((noreturn)); class DynamicLibraryOptions { public: - DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fInstallPathOverride(NULL) {} + DynamicLibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false) {} bool fWeakImport; bool fReExport; bool fBundleLoader; - const char* fInstallPathOverride; }; // @@ -71,7 +70,8 @@ public: kWeakReferenceMismatchNonWeak }; enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; - enum VersionMin { kMinUnset, k10_1, k10_2, k10_3, k10_4, k10_5 }; + enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; + enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; struct FileInfo { const char* path; @@ -94,13 +94,34 @@ public: 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(); OutputKind outputKind(); - bool stripLocalSymbols(); bool prebind(); bool bindAtLoad(); bool fullyLoadArchives(); @@ -113,6 +134,7 @@ public: uint64_t baseAddress(); bool keepPrivateExterns(); // only for kObjectFile bool interposable(); // only for kDynamicLibrary + bool needsModuleTable(); // only for kDynamicLibrary bool hasExportRestrictList(); bool allGlobalsAreDeadStripRoots(); bool shouldExport(const char*); @@ -122,13 +144,10 @@ public: bool traceArchives(); DeadStripMode deadStrip(); UndefinedTreatment undefinedTreatment(); - VersionMin macosxVersionMin(); + ObjectFile::ReaderOptions::VersionMin macosxVersionMin(); bool messagesPrefixedWithArchitecture(); Treatment picTreatment(); WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); - Treatment multipleDefinitionsInDylibs(); - Treatment overridingDefinitionInDependentDylib(); - bool warnOnMultipleDefinitionsInObjectFiles(); const char* umbrellaName(); std::vector& allowableClients(); const char* clientName(); @@ -142,19 +161,41 @@ public: 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(); - std::vector& traceSymbols(); FileInfo findFile(const char* path); - bool emitUUID(); + UUIDMode getUUIDMode() { return fUUIDMode; } bool warnStabs(); bool pauseAtEnd() { return fPause; } bool printStatistics() { return fStatistics; } bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; } - bool makeTentativeDefinitionsReal() { return fMakeTentativeDefinitionsReal; } + 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; } + 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 emitWarnings() { return !fSuppressWarnings; } private: class CStringEquals @@ -167,6 +208,22 @@ private: enum ExportMode { kExportDefault, kExportSome, kDontExportSome }; enum LibrarySearchMode { kSearchDylibAndArchiveInEachDir, kSearchAllDirsForDylibsThenAllDirsForArchives }; + class SetWithWildcards { + public: + void insert(const char*); + bool contains(const char*); + NameSet::iterator regularBegin() { return fRegular.begin(); } + NameSet::iterator regularEnd() { return fRegular.end(); } + private: + 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[]); @@ -178,21 +235,28 @@ private: 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, NameSet& set); + 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 setVersionMin(const char* version); void setWeakReferenceMismatchTreatment(const char* treatment); - void setDylibInstallNameOverride(const char* paths); + 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; @@ -202,70 +266,82 @@ private: OutputKind fOutputKind; bool fPrebind; bool fBindAtLoad; - bool fStripLocalSymbols; bool fKeepPrivateExterns; bool fInterposable; + bool fNeedsModuleTable; bool fIgnoreOtherArchFiles; bool fForceSubtypeAll; DeadStripMode fDeadStrip; - VersionMin fVersionMin; NameSpace fNameSpace; uint32_t fDylibCompatVersion; uint32_t fDylibCurrentVersion; const char* fDylibInstallName; + const char* fFinalName; const char* fEntryName; uint64_t fBaseAddress; - NameSet fExportSymbols; - NameSet fDontExportSymbols; + uint64_t fBaseWritableAddress; + bool fSplitSegs; + SetWithWildcards fExportSymbols; + SetWithWildcards fDontExportSymbols; ExportMode fExportMode; LibrarySearchMode fLibrarySearchMode; UndefinedTreatment fUndefinedTreatment; bool fMessagesPrefixedWithArchitecture; - Treatment fPICTreatment; WeakReferenceMismatchTreatment fWeakReferenceMismatchTreatment; - Treatment fMultiplyDefinedDynamic; - Treatment fMultiplyDefinedUnused; - bool fWarnOnMultiplyDefined; 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 fEmitUUID; bool fWarnStabs; bool fTraceDylibSearching; bool fPause; bool fStatistics; bool fPrintOptions; - bool fMakeTentativeDefinitionsReal; + bool fSharedRegionEligible; + bool fPrintOrderFileStatistics; + bool fReadOnlyx86Stubs; + bool fPositionIndependentExecutable; + bool fMaxMinimumHeaderPad; + bool fDeadStripDylibs; + bool fSuppressWarnings; std::vector fInitialUndefines; + NameSet fAllowedUndefined; NameSet fWhyLive; - std::vector fTraceSymbols; - unsigned long fLimitUndefinedSymbols; std::vector fExtraSections; std::vector fSectionAlignments; + std::vector fOrderedSymbols; + std::vector fCustomSegmentAddresses; + std::vector fCustomSegmentProtections; + std::vector fDylibOverrides; std::vector fLibrarySearchPaths; std::vector fFrameworkSearchPaths; std::vector fSDKPaths; - bool fAllowStackExecute; - + bool fSaveTempFiles; }; - #endif // __OPTIONS__ diff --git a/src/ld.cpp b/src/ld.cpp index d088f9a..ff53037 100644 --- a/src/ld.cpp +++ b/src/ld.cpp @@ -1,6 +1,5 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -35,7 +34,7 @@ #include #include #include - +#include #include #include @@ -45,6 +44,8 @@ #include #include #include +#include +#include #include "Options.h" @@ -55,93 +56,15 @@ #include "MachOReaderDylib.hpp" #include "MachOWriterExecutable.hpp" -#include "SectCreate.h" - -#if 0 -static void dumpAtom(ObjectFile::Atom* atom) -{ - //printf("atom: %p\n", atom); - - // name - printf("name: %s\n", atom->getDisplayName()); - - // scope - 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 - switch ( atom->getDefinitinonKind() ) { - 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; - default: - printf("scope: unknown\n"); - } - - // segment and section - printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); - - // attributes - printf("attrs: "); - if ( atom->dontDeadStrip() ) - printf("dont-dead-strip "); - if ( atom->isZeroFill() ) - printf("zero-fill "); - printf("\n"); - - // size - printf("size: 0x%012llX\n", atom->getSize()); - - // content - uint8_t content[atom->getSize()]; - atom->copyRawContent(content); - printf("content: "); - if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { - printf("\"%s\"", content); - } - else { - for (unsigned int i=0; i < sizeof(content); ++i) - printf("%02X ", content[i]); - } - printf("\n"); +#define LLVM_SUPPORT 0 + +#if LLVM_SUPPORT +#include "LLVMReader.hpp" +#endif - // references - 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()); - } - // attributes +#include "OpaqueSection.hpp" -} - -#endif class CStringComparor { @@ -160,7 +83,7 @@ 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); @@ -169,6 +92,7 @@ private: bool operator()(Section* left, Section* right); }; + typedef __gnu_cxx::hash_map, CStringEquals> NameToOrdinal; typedef __gnu_cxx::hash_map, CStringEquals> NameToSection; //typedef std::map NameToSection; @@ -178,15 +102,18 @@ private: 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) { - //fprintf(stderr, "new Section(%s, %s)\n", sectionName, segmentName); + 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) @@ -204,7 +131,6 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze // does not exist, so make a new one Section* sect = new Section(sectionName, segmentName, zeroFill); - sect->fIndex = fgMapping.size(); fgMapping[sectionName] = sect; fgSections.push_back(sect); @@ -213,6 +139,10 @@ Section* Section::find(const char* sectionName, const char* segmentName, bool ze find("__textcoal_nt", "__TEXT", false); } + // remember segment discovery order + if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() ) + fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size(); + return sect; } @@ -226,29 +156,24 @@ int Section::Sorter::segmentOrdinal(const char* segName) 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 5; + return fgSegmentDiscoverOrder[segName]+6; } bool Section::Sorter::operator()(Section* left, Section* right) { // Segment is primary sort key - const char* leftSegName = left->fSegmentName; - const char* rightSegName = right->fSegmentName; - int segNameCmp = strcmp(leftSegName, rightSegName); - if ( segNameCmp != 0 ) - { - int leftSegOrdinal = segmentOrdinal(leftSegName); - int rightSegOrdinal = segmentOrdinal(rightSegName); - if ( leftSegOrdinal < rightSegOrdinal ) - return true; - if ( leftSegOrdinal == rightSegOrdinal ) - return segNameCmp < 0; + 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 ) @@ -261,8 +186,8 @@ bool Section::Sorter::operator()(Section* left, Section* right) } void Section::assignIndexes() -{ - //printf("unsorted:\n"); +{ + //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); //} @@ -275,13 +200,13 @@ void Section::assignIndexes() for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) (*it)->fIndex = newOrder++; - //printf("sorted:\n"); + //printf("sorted sections:\n"); //for (std::vector::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: name=%s\n", (*it)->fSectionName); + // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName); //} } -class Linker { +class Linker : public ObjectFile::Reader::DylibHander { public: Linker(int argc, const char* argv[]); @@ -291,10 +216,13 @@ public: bool isInferredArchitecture(); void createReaders(); void createWriter(); - void addInputFile(ObjectFile::Reader* reader); + 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 @@ -307,13 +235,20 @@ private: void addAtom(ObjectFile::Atom& atom); void addAtoms(std::vector& atoms); void buildAtomList(); + void processDylibs(); + void updateContraints(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(); @@ -332,17 +267,18 @@ private: char* commatize(uint64_t in, char* out); void getVMInfo(vm_statistics_data_t& info); cpu_type_t inferArchitecture(); - - void resolve(ObjectFile::Reference* reference); - void resolveFrom(ObjectFile::Reference* reference); - void addJustInTimeAtoms(const char* name); + 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); + 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 addIndirectLibraries(ObjectFile::Reader* reader); - bool haveIndirectLibrary(const char* path, ObjectFile::Reader* reader); - bool haveDirectLibrary(const char* path); void logTraceInfo(const char* format, ...); @@ -356,20 +292,34 @@ private: ObjectFile::Atom* find(const char* name); unsigned int getRequireCount() { return fRequireCount; } void getNeededNames(bool andWeakDefintions, std::vector& undefines); - private: typedef __gnu_cxx::hash_map, CStringEquals> Mapper; + private: Linker& fOwner; Mapper fTable; unsigned int fRequireCount; }; - struct AtomSorter + class AtomSorter { - bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right); + 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; @@ -382,33 +332,42 @@ private: Options fOptions; SymbolTable fGlobalSymbolTable; - unsigned int fWeakSymbolsAddedCount; + uint32_t fNextInputOrdinal; std::vector fInputFiles; ExecutableFile::Writer* fOutputFile; - std::vector fDynamicLibraries; - std::list fIndirectDynamicLibraries; + 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; - unsigned int fNextSortOrder; - unsigned int fNextObjectFileOrder; 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 fStartLoadUndefinesTime; - uint64_t fStartResolveTime; + uint64_t fStartLoadAndResolveTime; uint64_t fStartSortTime; uint64_t fStartDebugTime; uint64_t fStartWriteTime; @@ -419,14 +378,21 @@ private: 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), fOutputFile(NULL), fCreateUUID(false), fNextSortOrder(1), - fNextObjectFileOrder(1), fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), - fOutputFileSize(0), fTotalObjectSize(0), - fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0) + : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), + fCreateUUID(false), 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() ) @@ -485,19 +451,19 @@ cpu_type_t Linker::inferArchitecture() ::close(fd); if ( amount >= (ssize_t)sizeof(buffer) ) { if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //fprintf(stderr, "ld64 warning: -arch not used, infering -arch ppc based on %s\n", it->path); + //fprintf(stderr, "ld: warning -arch not used, infering -arch ppc based on %s\n", it->path); return CPU_TYPE_POWERPC; } else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //fprintf(stderr, "ld64 warning: -arch not used, infering -arch ppc64 based on %s\n", it->path); + //fprintf(stderr, "ld: warning -arch not used, infering -arch ppc64 based on %s\n", it->path); return CPU_TYPE_POWERPC64; } else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //fprintf(stderr, "ld64 warning: -arch not used, infering -arch i386 based on %s\n", it->path); + //fprintf(stderr, "ld: warning -arch not used, infering -arch i386 based on %s\n", it->path); return CPU_TYPE_I386; } else if ( mach_o::relocatable::Reader::validFile(buffer) ) { - //fprintf(stderr, "ld64 warning: -arch not used, infering -arch x86_64 based on %s\n", it->path); + //fprintf(stderr, "ld: warning -arch not used, infering -arch x86_64 based on %s\n", it->path); return CPU_TYPE_X86_64; } } @@ -505,7 +471,7 @@ cpu_type_t Linker::inferArchitecture() } // no thin .o files found, so default to same architecture this was built as - fprintf(stderr, "ld64 warning: -arch not specified\n"); + fprintf(stderr, "ld: warning -arch not specified\n"); #if __ppc__ return CPU_TYPE_POWERPC; #elif __i386__ @@ -520,10 +486,10 @@ cpu_type_t Linker::inferArchitecture() } -void Linker::addInputFile(ObjectFile::Reader* reader) +void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info) { - reader->setSortOrder(fNextObjectFileOrder++); fInputFiles.push_back(reader); + fDylibOptionsMap[reader] = info.options; } void Linker::setOutputFile(ExecutableFile::Writer* writer) @@ -546,6 +512,7 @@ private: void Linker::loadAndResolve() { + fStartLoadAndResolveTime = mach_absolute_time(); if ( fOptions.deadStrip() == Options::kDeadStripOff ) { // without dead-code-stripping: // find atoms to resolve all undefines @@ -566,12 +533,48 @@ void Linker::loadAndResolve() } } +void Linker::optimize() +{ + std::vector newAtoms; + + const int readerCount = fInputFiles.size(); + for (int i=0; i < readerCount; ++i) { + fInputFiles[i]->optimize(fAllAtoms, newAtoms, fNextInputOrdinal); + } + // note: When writer start generating stubs and non-lazy-pointers for all architecture, do not insert + // newAtoms into fGlobalSymbolTable. Instead directly insert them in fAllAtoms and set their order appropriately. + this->addAtoms(newAtoms); + + // Some of the optimized atoms may not have identified section properly, if they + // were created before optimizer produces corrosponding real atom. Here, input + // file readers are not able to patch it themselves because Section::find() is + // linker specific. + for(std::vector::iterator itr = fAllAtoms.begin(); + itr != fAllAtoms.end(); ++itr) { + + ObjectFile::Atom *atom = *itr; + if (atom->getSectionName() && !atom->getSection()) + atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill())); + } + + if ( fOptions.deadStrip() != Options::kDeadStripOff ) { + fLiveAtoms.clear(); + deadStripResolve(); + } + else + resolveReferences(); +} + void Linker::link() { this->buildAtomList(); this->loadAndResolve(); - this->sortAtoms(); + this->optimize(); + this->checkObjC(); + this->processDTrace(); this->tweakLayout(); + this->sortSections(); + this->sortAtoms(); this->writeDotOutput(); this->collectDebugInfo(); this->writeOutput(); @@ -587,7 +590,7 @@ void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) if ( sUnitsPerSecond == 0 ) { struct mach_timebase_info timeBaseInfo; if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { - sUnitsPerSecond = 1000000000LL * timeBaseInfo.denom / timeBaseInfo.numer; + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); } } @@ -641,13 +644,12 @@ void Linker::printStatistics() getVMInfo(endVMInfo); uint64_t totalTime = fEndTime - fStartTime; - printTime("ld64 total time", totalTime, totalTime); + 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", fStartLoadUndefinesTime - fStartBuildAtomsTime, totalTime); - printTime(" load undefines", fStartResolveTime - fStartLoadUndefinesTime, totalTime); - printTime(" resolve references", fStartSortTime - fStartResolveTime, 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); @@ -671,11 +673,18 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) std::vector& references = atom.getReferences(); for (std::vector::iterator it=references.begin(); it != references.end(); it++) { ObjectFile::Reference* reference = *it; - if ( reference->isTargetUnbound() ) { + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) fGlobalSymbolTable.require(reference->getTargetName()); - } - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) + 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 { @@ -687,8 +696,6 @@ 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) ) { - fGlobalSymbolTable.add(atom); - // update scope based on export list (possible that globals are downgraded to private_extern) if ( (scope == ObjectFile::Atom::scopeGlobal) && fOptions.hasExportRestrictList() ) { bool doExport = fOptions.shouldExport(name); @@ -696,33 +703,107 @@ inline void Linker::addAtom(ObjectFile::Atom& atom) atom.setScope(ObjectFile::Atom::scopeLinkageUnit); } } + // add to symbol table + fGlobalSymbolTable.add(atom); } // record section orders so output file can have same order - atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); + if (atom.getSectionName()) + atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill())); +} + +void Linker::updateContraints(ObjectFile::Reader* reader) +{ + // check objc objects were compiled compatibly + ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint(); + if ( reader->getInstallPath() == NULL ) { + // adding a .o file + switch ( fCurrentObjCConstraint ) { + case ObjectFile::Reader::kObjcNone: + fCurrentObjCConstraint = objcAddition; + break; + case ObjectFile::Reader::kObjcRetainRelease: + case ObjectFile::Reader::kObjcRetainReleaseOrGC: + case ObjectFile::Reader::kObjcGC: + if ( (fCurrentObjCConstraint != objcAddition) && (objcAddition != ObjectFile::Reader::kObjcNone) ) + throwf("%s built with different Garbage Collection settings", reader->getPath()); + break; + } + } + if ( reader->objcReplacementClasses() ) + fObjcReplacmentClasses = true; - // assign order in which this atom was originally seen - if ( atom.getSortOrder() == 0 ) - fNextSortOrder = atom.setSortOrder(fNextSortOrder); + // check cpu sub-types + ObjectFile::Reader::CpuConstraint cpuAddition = reader->getCpuConstraint(); + switch ( fCurrentCpuConstraint ) { + case ObjectFile::Reader::kCpuAny: + fCurrentCpuConstraint = cpuAddition; + break; + case ObjectFile::Reader::kCpuG3: + switch ( cpuAddition ) { + case ObjectFile::Reader::kCpuAny: + case ObjectFile::Reader::kCpuG3: + break; + case ObjectFile::Reader::kCpuG4: + case ObjectFile::Reader::kCpuG5: + // previous file for G3 this one is more contrained, use it + fCurrentCpuConstraint = cpuAddition; + break; + } + break; + case ObjectFile::Reader::kCpuG4: + switch ( cpuAddition ) { + case ObjectFile::Reader::kCpuAny: + case ObjectFile::Reader::kCpuG3: + case ObjectFile::Reader::kCpuG4: + break; + case ObjectFile::Reader::kCpuG5: + // previous file for G5 this one is more contrained, use it + fCurrentCpuConstraint = cpuAddition; + break; + } + break; + case ObjectFile::Reader::kCpuG5: + // G5 can run everything + break; + } } inline void Linker::addAtoms(std::vector& atoms) { - bool first = true; // assume all atoms are from same reader + bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives; + bool first = true; for (std::vector::iterator it=atoms.begin(); it != atoms.end(); it++) { - if ( first ) { + // 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); - } + updateContraints(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(); @@ -736,15 +817,19 @@ void Linker::buildAtomList() this->addAtoms(fOutputFile->getAtoms()); // each reader contributes atoms - const int readerCount = fInputFiles.size(); - for (int i=0; i < readerCount; ++i) { - this->addAtoms(fInputFiles[i]->getAtoms()); + 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(SectCreate::MakeReader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)->getAtoms()); + this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); + fNextInputOrdinal += it->dataLen; } } @@ -759,7 +844,6 @@ static const char* pathLeafName(const char* path) void Linker::loadUndefines() { - fStartLoadUndefinesTime = mach_absolute_time(); // keep looping until no more undefines were added in last loop unsigned int undefineCount = 0xFFFFFFFF; while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { @@ -770,134 +854,199 @@ void Linker::loadUndefines() 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)) ) - this->addJustInTimeAtoms(name); + || ((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; + } } } } -void Linker::checkUndefines() +// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names +class ExportedObjcClass { - if ( fOptions.outputKind() != Options::kObjectFile ) { - // 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; +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; } - std::vector unresolvableUndefines; - fGlobalSymbolTable.getNeededNames(false, unresolvableUndefines); - 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->isTargetUnbound() ) { - if ( strcmp(reference->getTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = 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->hasFromTarget() && reference->isFromTargetUnbound() ) { - if ( strcmp(reference->getFromTargetName(), 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; - } + } + // scan command line options + if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { + fprintf(stderr, " -exported_symbols_list command line option\n"); + ++unresolvableExportsCount; } } - if ( doError && (unresolvableCount > unresolvableExportsCount) ) // last check should be removed. It exists so broken projects still build - throw "symbol(s) not found"; } + if ( doError ) + throw "symbol(s) not found"; } } -void Linker::addJustInTimeAtoms(const char* name) +std::vector* Linker::addJustInTimeAtoms(const char* name) { // 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); - delete atoms; //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); - return; // found a definition, no need to search anymore + return atoms; // found a definition, no need to search anymore } } - // give direct readers a chance + // 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() ); std::vector* atoms = reader->getJustInTimeAtomsFor(name); if ( atoms != NULL ) { this->addAtoms(*atoms); - delete atoms; - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fInputFiles[i]->getPath() ); - return; // found a definition, no need to search anymore + //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); + if ( fOptions.readerOptions().fTraceArchives ) { + logArchive(reader); + } + // if this is a weak definition in a dylib + if ( (atoms->size() == 1) && (reader->getInstallPath() != NULL) && (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; + } } } } - // give indirect readers a chance - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - ObjectFile::Reader* reader = it->reader; - // for two-level namespace, only search re-exported indirect libraries - if ( (reader != NULL) && ((it->reExportedViaDirectLibrary != NULL) || (fOptions.nameSpace() != Options::kTwoLevelNameSpace)) ) { - std::vector* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - delete atoms; - return; // found a definition, no need to search anymore + // 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 + } } } } // when creating .o file, writer goes last (this is so any static archives will be searched above) - if ( (fOptions.outputKind() == Options::kObjectFile) || (fOptions.undefinedTreatment() != Options::kUndefinedError) ) { + if ( (fOptions.outputKind() == Options::kObjectFile) + || (fOptions.undefinedTreatment() != Options::kUndefinedError) + || fOptions.someAllowedUndefines() ) { ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); if ( atom != NULL ) { this->addAtom(*atom); - return; + return NULL; } } //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); + return NULL; } void Linker::resolve(ObjectFile::Reference* reference) @@ -925,16 +1074,15 @@ void Linker::resolveFrom(ObjectFile::Reference* reference) void Linker::resolveReferences() { - fStartResolveTime = mach_absolute_time(); // 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->isTargetUnbound() ) + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) this->resolve(reference); - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) + if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) this->resolveFrom(reference); } } @@ -974,6 +1122,24 @@ private: }; +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) { @@ -992,17 +1158,23 @@ void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* p 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->isTargetUnbound() ) { + 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->addJustInTimeAtoms(targetName); + this->addJustInTimeAtomsAndMarkLive(targetName); } // look again target = fGlobalSymbolTable.find(targetName); @@ -1015,34 +1187,48 @@ void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* p fGlobalSymbolTable.require(targetName); } } - if ( ! reference->isTargetUnbound() ) { - thisChain.name = reference->getTargetName(); - markLive(reference->getTarget(), &thisChain); + 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; } - if ( reference->hasFromTarget() ) { - // do the same as above, for from target - if ( reference->isFromTargetUnbound() ) { - // look in global symbol table - const char* targetName = reference->getFromTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { - // load archives or dylibs - this->addJustInTimeAtoms(targetName); - } - // look again - target = fGlobalSymbolTable.find(targetName); - if ( target != NULL ) { - reference->setFromTarget(*target); - } - else { - // mark as undefined, for later error processing - fGlobalSymbolTable.require(targetName); - } + // 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); } - if ( ! reference->isFromTargetUnbound() ) { + } + 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; } } } @@ -1053,7 +1239,7 @@ void Linker::addLiveRoot(const char* name) { ObjectFile::Atom* target = fGlobalSymbolTable.find(name); if ( target == NULL ) { - this->addJustInTimeAtoms(name); + this->addJustInTimeAtomsAndMarkLive(name); target = fGlobalSymbolTable.find(name); } if ( target != NULL ) @@ -1082,7 +1268,7 @@ void Linker::deadStripResolve() if ( fOptions.allGlobalsAreDeadStripRoots() ) { for (std::vector::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { ObjectFile::Atom* atom = *it; - if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) + if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) ) fLiveRootAtoms.insert(atom); } } @@ -1103,7 +1289,7 @@ void Linker::deadStripResolve() std::vector& references = (*it)->getReferences(); for (std::vector::iterator rit=references.begin(); rit != references.end(); rit++) { ObjectFile::Reference* reference = *rit; - if ( reference->isTargetUnbound() ) { + if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); if ( target != NULL ) { reference->setTarget(*target, reference->getTargetOffset()); @@ -1114,32 +1300,607 @@ void Linker::deadStripResolve() fprintf(stderr, "warning: ld64 internal error %s is not a tentative definition\n", target->getDisplayName()); } } - if ( reference->hasFromTarget() && reference->isFromTargetUnbound() ) { - 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 ) - fprintf(stderr, "warning: ld64 internal error %s is not a tentative definition\n", 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 ) + fprintf(stderr, "warning: ld64 internal error %s is not a tentative definition\n", 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; + } + 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; + } + 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) { + 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) { + 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\n", 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\n", 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; + } + // 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\n", 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() ) + fprintf(stderr, "ld: warning %s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate\n", 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() ) + fprintf(stderr, "ld: warning %s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate\n", 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 + fprintf(stderr, "ld: warning can't order %s because both %s and %s must follow it\n", + 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 ) { + fprintf(stderr, "ld: warning can't order %s because both %s and %s must preceed it\n", + 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) ) { + fprintf(stderr, "ld: warning only %u out of %lu order_file symbols were applicable\n", matchCount, fOptions.orderedSymbols().size() ); } } - - // now remove all non-live atoms from fAllAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); -} -void Linker::sortAtoms() -{ - fStartSortTime = mach_absolute_time(); - Section::assignIndexes(); - std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter()); + // 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%s\n", (*it)->getDisplayName()); + // fprintf(stderr, "\t%p, %u %s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName()); //} } @@ -1147,6 +1908,21 @@ void Linker::sortAtoms() // 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 + 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) && atom->getSegment().isContentReadable() ) + atom->setSection(hugeZeroFills); + } + } } @@ -1224,7 +2000,7 @@ void Linker::writeDotOutput() fclose(out); } else { - fprintf(stderr, "ld64 warning: could not write dot output file: %s\n", dotOutFilePath); + fprintf(stderr, "ld: warning could not write dot output file: %s\n", dotOutFilePath); } } } @@ -1402,7 +2178,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader, std::mapgetPath()); + fprintf(stderr, "ld: warning EINCL missing BINCL in %s\n", reader->getPath()); } else { ranges[curRangeIndex].end = it+1; @@ -1436,7 +2212,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader, std::mapstring, reader->getPath()); + fprintf(stderr, "ld: cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); } break; case N_SO: @@ -1474,7 +2250,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader, std::mapgetPath()); if ( curRangeIndex != -1 ) - fprintf(stderr, "ld64 warning: BINCL (%s) missing EINCL in %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); + fprintf(stderr, "ld: warning BINCL (%s) missing EINCL in %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); // if no BINCLs if ( ranges.size() == 0 ) { @@ -1569,7 +2345,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader, std::map 0) ) { + if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { // starting SO is associated with first atom stab.atom = soRanges[soIndex].first; } @@ -1592,7 +2368,7 @@ void Linker::collectStabs(ObjectFile::Reader* reader, std::map& readersWithDwarfOrdinals) + NoDebugNoteAtom(const std::map& readersWithDwarfOrdinals) : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} bool operator()(const ObjectFile::Atom* atom) const { @@ -1613,8 +2389,8 @@ private: class ReadersWithDwarfSorter { public: - ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, - const std::map& atomOrdinals) + ReadersWithDwarfSorter(const std::map& readersWithDwarfOrdinals, + const std::map& atomOrdinals) : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const @@ -1646,6 +2422,7 @@ void Linker::synthesizeDebugNotes(std::vector& allAtoms 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; @@ -1692,7 +2469,7 @@ void Linker::synthesizeDebugNotes(std::vector& allAtoms objStab.type = N_OSO; objStab.other = 0; objStab.desc = 1; - objStab.value = atom->getFile()->getModificationTime(); + objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); objStab.string = assureFullPath(atom->getFile()->getPath()); fStabs.push_back(objStab); wroteStartSO = true; @@ -1839,10 +2616,10 @@ void Linker::collectDebugInfo() } } } - + if ( someDwarf || someStabs ) { // try to minimize re-allocations - fStabs.reserve(1024); + fStabs.reserve(1024); // make mapping from atoms to ordinal uint32_t ordinal = 1; @@ -1850,7 +2627,7 @@ void Linker::collectDebugInfo() atomOrdinals[*it] = ordinal++; } } - + // process all dwarf .o files as a batch if ( someDwarf ) { // make mapping from readers with dwarf to ordinal @@ -1864,18 +2641,18 @@ void Linker::collectDebugInfo() readersWithDwarfOrdinals[reader] = readerOrdinal++; } } - - // make a vector of atoms + + // 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(), + 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 @@ -1908,9 +2685,14 @@ void Linker::collectDebugInfo() 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(), this->dyldHelper(), (fCreateUUID && fOptions.emitUUID())); + fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(), this->dyldHelper(), + fCreateUUID, fCanScatter, + fCurrentCpuConstraint, fBiggerThanTwoGigOutput); } ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) @@ -1957,38 +2739,43 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) switch (fArchitecture) { case CPU_TYPE_POWERPC: if ( mach_o::relocatable::Reader::validFile(p) ) - return this->addObject(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); + 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(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len); + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::archive::Reader::validFile(p, len) ) - return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + return this->addArchive(new mach_o::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(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); + 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(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len); + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::archive::Reader::validFile(p, len) ) - return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + return this->addArchive(new mach_o::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(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); + 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(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len); + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::archive::Reader::validFile(p, len) ) - return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + return this->addArchive(new mach_o::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(mach_o::relocatable::Reader::make(p, info.path, info.modTime, fOptions.readerOptions()), info, len); + 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(mach_o::dylib::Reader::make(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions()), info, len); + return this->addDylib(new mach_o::dylib::Reader::Reader(p, len, info.path, info.options.fBundleLoader, fOptions.readerOptions(), fNextInputOrdinal), info, len); else if ( mach_o::archive::Reader::validFile(p, len) ) - return this->addArchive(mach_o::archive::Reader::make(p, len, info.path, info.modTime, fOptions.readerOptions()), info, len); + return this->addArchive(new mach_o::archive::Reader::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); break; } +#if LLVM_SUPPORT + if ( LLVMReader::validFile(p, info.path, fArchitecture, fOptions) ) { + return this->addObject(LLVMReader::make(p, info.path, info.modTime, fOptions), info, len); + } +#endif // error handling if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { throwf("missing required architecture %s in file", fArchitectureName); @@ -1998,6 +2785,121 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) } } +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) { + fprintf(stderr, "ld: warning ignoring -dylib_file option, %s\n", 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() { @@ -2012,7 +2914,7 @@ void Linker::createReaders() // 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)); + this->addInputFile(this->createReader(entry), entry); } catch (const char* msg) { if ( strstr(msg, "architecture") != NULL ) { @@ -2020,7 +2922,7 @@ void Linker::createReaders() // ignore, because this is about an architecture not in use } else { - fprintf(stderr, "ld64 warning: in %s, %s\n", entry.path, msg); + fprintf(stderr, "ld: warning in %s, %s\n", entry.path, msg); } } else { @@ -2030,115 +2932,16 @@ void Linker::createReaders() } } - // add first level of indirect dylibs - fDirectLibrariesComplete = true; - for (std::vector::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) { - this->addIndirectLibraries(it->reader); - } - - // indirect handling depends on namespace - switch ( fOptions.nameSpace() ) { - case Options::kFlatNameSpace: - case Options::kForceFlatNameSpace: - // with flat namespace, blindly load all indirect libraries - // the indirect list will grow as indirect libraries are loaded - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - try { - it->reader = this->createReader(fOptions.findFile(it->path)); - it->reader->setSortOrder(fNextObjectFileOrder++); - } - catch (const char* msg) { - fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg); - } - } - break; - - case Options::kTwoLevelNameSpace: - // with two-level namespace we only want to use indirect libraries that are re-exported through a library that is used - { - bool indirectAdded = true; - while ( indirectAdded ) { - indirectAdded = false; - // instantiate a reader for each indirect library and try to find parent that re-exports it - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - if ( it->reader == NULL ) { - try { - it->reader = this->createReader(fOptions.findFile(it->path)); - it->reader->setSortOrder(fNextObjectFileOrder++); - indirectAdded = true; - } - catch (const char* msg) { - fprintf(stderr, "ld64 warning: indirect library %s could not be loaded: %s\n", it->path, msg); - } - } - // if an indirect library does not have an assigned parent, look for one - if ( (it->reader != NULL) && (it->reExportedViaDirectLibrary == NULL) ) { - it->reExportedViaDirectLibrary = this->findDirectLibraryWhichReExports(*it); - } - } - } - } - break; - } - - // add relevant indirect libraries to the end of fDynamicLibraries - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - if ( (it->reader != NULL) && (it->reExportedViaDirectLibrary != NULL) || (fOptions.nameSpace() != Options::kTwoLevelNameSpace) ) { - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = it->reader; - dylibInfo.options.fWeakImport = false; - dylibInfo.options.fReExport = false; - dylibInfo.options.fInstallPathOverride = NULL; - dylibInfo.indirect = true; - dylibInfo.directReader = it->reExportedViaDirectLibrary; - fDynamicLibraries.push_back(dylibInfo); - if ( fOptions.readerOptions().fTraceIndirectDylibs ) { - const char* fullPath = it->reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); - } - } - } -} - - -ObjectFile::Reader* Linker::findDirectLibraryWhichReExports(IndirectLibrary& indirectLib) -{ - // ask each parent if they re-export this dylib - for (std::set::iterator pit=indirectLib.parents.begin(); pit != indirectLib.parents.end(); pit++) { - if ( (*pit)->reExports(indirectLib.reader) ) { - ObjectFile::Reader* lib = *pit; - // first check if we found a direct library, if so return it - for (std::vector::iterator dit=fDynamicLibraries.begin(); dit != fDynamicLibraries.end(); dit++) { - if ( dit->reader == lib && dit->indirect == false ) - return lib; - } - // otherwise search indirects for parent and see how it is reexported - for (std::list::iterator iit=fIndirectDynamicLibraries.begin(); iit != fIndirectDynamicLibraries.end(); iit++) { - if ( iit->reader == lib ) { - ObjectFile::Reader* lib2 = this->findDirectLibraryWhichReExports(*iit); - if ( lib2 != NULL ) - return lib2; - } - } - } - } - return NULL; + this->processDylibs(); } ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { - if (fOptions.readerOptions().fTraceArchives) { - 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); - } + fNextInputOrdinal += mappedLen; + // remember which readers are archives because they are logged differently + fArchiveReaders.insert(reader); // update stats fTotalArchiveSize += mappedLen; @@ -2148,57 +2951,49 @@ ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options 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; } -ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { - // this is a "blank" stub - // silently ignore it - return reader; - } - if ( fDirectLibrariesComplete ) { - this->addIndirectLibraries(reader); - } - else { - if ( fOptions.readerOptions().fTraceDylibs ) { - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); - } - ExecutableFile::DyLibUsed dylibInfo; - dylibInfo.reader = reader; - dylibInfo.options = info.options; - dylibInfo.indirect = false; - dylibInfo.directReader = NULL; - fDynamicLibraries.push_back(dylibInfo); - - - // Verify that a client is allowed to link to this dylib. There are three cases. - bool okToLink = true; - const char* outputFilePath = fOptions.installPath(); - const char* outputFilePathLastSlash = strrchr(outputFilePath, '/'); - if ( reader->parentUmbrella() != NULL ) { +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 - okToLink = ( (outputFilePathLastSlash != NULL) && (strcmp(&outputFilePathLastSlash[1], reader->parentUmbrella()) == 0) ); - } - - if ( !okToLink && (reader->parentUmbrella() != NULL) ) { + 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 - okToLink = ( (outputFilePathLastSlash != NULL) - && (fOptions.umbrellaName() != NULL) - && (strcmp(fOptions.umbrellaName(), reader->parentUmbrella()) == 0) ); + isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) ); } - std::vector* clients = reader->getAllowableClients(); - if ( !okToLink && (clients != NULL) ) { + 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; @@ -2207,96 +3002,82 @@ ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options:: clientNameLen = strlen(clientName); } else { - // infer client name from output path (e.g. xxx/libfoo.A.dylib --> foo, Bar.framework/Bar --> Bar) - clientName = outputFilePath; + // 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 ( outputFilePathLastSlash != NULL ) - clientName = &outputFilePathLastSlash[1]; + 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 = strlen(clientName); - else + 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 ) - okToLink = true; + isAllowableClient = true; } } - - // error out if we are not allowed to link - if ( ! okToLink ) - //throwf("'%s' is a subframework. Link against the umbrella framework '%s.framework' instead.", - fprintf(stderr, "'%s' is a subframework. Link against the umbrella framework '%s.framework' instead.", - reader->getPath(), reader->parentUmbrella()); - } - - // update stats - ++fTotalDylibsLoaded; - - return reader; -} - - -void Linker::addIndirectLibraries(ObjectFile::Reader* reader) -{ - std::vector* dependentLibs = reader->getDependentLibraryPaths(); - if ( dependentLibs != NULL ) { - for (std::vector::iterator it=dependentLibs->begin(); it != dependentLibs->end(); it++) { - if ( this->haveDirectLibrary(*it) ) { - // do nothing, direct library already exists - } - else if ( this->haveIndirectLibrary(*it, reader) ) { - // side effect of haveIndirectLibrary() added reader to parent list + + 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 { - // add to list of indirect libraries - IndirectLibrary indirectLib; - indirectLib.path = *it; - indirectLib.fileLen = 0; - indirectLib.reader = NULL; - indirectLib.parents.insert(reader); - indirectLib.reExportedViaDirectLibrary = NULL; - fIndirectDynamicLibraries.push_back(indirectLib); - //fprintf(stderr, "add indirect library: %s\n", *it); + throwf("cannot link directly with %s", reader->getPath()); } } } + + } -bool Linker::haveIndirectLibrary(const char* path, ObjectFile::Reader* parentReader) +ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) { - for (std::list::iterator it=fIndirectDynamicLibraries.begin(); it != fIndirectDynamicLibraries.end(); it++) { - if ( strcmp(path, it->path) == 0 ) { - it->parents.insert(parentReader); - return true; + 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; } - if ( it->reader != NULL ) { - const char* installPath = it->reader->getInstallPath(); - if ( (installPath != NULL) && (strcmp(path, installPath) == 0) ) - return true; + else { + InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath()); + if ( pos2 == fDylibMap.end() ) + fDylibMap[strdup(reader->getPath())] = reader; + else + fprintf(stderr, "ld: warning, duplicate dylib %s\n", reader->getPath()); } } - return false; -} + else if ( info.options.fBundleLoader ) + fBundleLoaderReader = reader; -bool Linker::haveDirectLibrary(const char* path) -{ - for (std::vector::iterator it=fDynamicLibraries.begin(); it != fDynamicLibraries.end(); it++) { - if ( strcmp(path, it->reader->getPath()) == 0 ) - return true; - const char* installPath = it->reader->getInstallPath(); - if ( (installPath != NULL) && (strcmp(path, installPath) == 0) ) - return true; - } - return false; + // 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; @@ -2338,19 +3119,71 @@ void Linker::logTraceInfo (const char* format, ...) 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, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); break; case CPU_TYPE_POWERPC64: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); break; case CPU_TYPE_I386: - this->setOutputFile(new mach_o::executable::Writer(path, fOptions, fDynamicLibraries)); + 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, fDynamicLibraries)); + this->setOutputFile(new mach_o::executable::Writer(path, fOptions, dynamicLibraries)); break; default: throw "unknown architecture"; @@ -2374,32 +3207,43 @@ void Linker::SymbolTable::require(const char* name) } // convenience labels for 2-dimensional switch statement -enum { +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 + 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) @@ -2413,7 +3257,7 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) existingAtom = pos->second; if ( existingAtom != NULL ) { // already have atom with same name in symbol table - switch ( (existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind() ) { + switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) { case kRegAndReg: throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); case kRegAndWeak: @@ -2423,6 +3267,12 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) case kRegAndTent: // ignore new tentative atom, because we already have a regular one useNew = false; + if ( newAtom.getSize() > existingAtom->getSize() ) { + fprintf(stderr, "ld: warning for symbol %s tentative definition of size %llu from %s is " + "is smaller than the real definition of size %llu from %s\n", + newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(), + existingAtom->getSize(), existingAtom->getFile()->getPath()); + } break; case kRegAndExtern: // ignore external atom, because we already have a one @@ -2432,13 +3282,16 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // ignore external atom, because we already have a one useNew = false; break; + case kRegAndAbsolute: + throwf("duplicate symbol %s in %s and %s\n", 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().leadingZeros() > existingAtom->getAlignment().leadingZeros() ); + useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() ); break; case kWeakAndTent: // replace existing weak atom with tentative one ??? @@ -2451,8 +3304,17 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // 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 + if ( newAtom.getSize() < existingAtom->getSize() ) { + fprintf(stderr, "ld: warning for symbol %s tentative definition of size %llu from %s is " + "being replaced by a real definition of size %llu from %s\n", + newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), + newAtom.getSize(), newAtom.getFile()->getPath()); + } break; case kTentAndWeak: // replace existing tentative atom with weak one ??? @@ -2461,9 +3323,10 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // use largest if ( newAtom.getSize() < existingAtom->getSize() ) { useNew = false; - } else { - if ( newAtom.getAlignment().leadingZeros() < existingAtom->getAlignment().leadingZeros() ) - fprintf(stderr, "ld64 warning: alignment lost in merging tentative definition %s\n", newAtom.getDisplayName()); + } + else { + if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() ) + fprintf(stderr, "ld: warning alignment lost in merging tentative definition %s\n", newAtom.getDisplayName()); } break; case kTentAndExtern: @@ -2472,13 +3335,13 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) switch ( fOwner.fOptions.commonsMode() ) { case Options::kCommonsIgnoreDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", + fprintf(stderr, "ld: using common symbol %s from %s and ignoring defintion from dylib %s\n", existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); useNew = false; break; case Options::kCommonsOverriddenByDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing common symbol %s from %s with true definition from dylib %s\n", + fprintf(stderr, "ld: replacing common symbol %s from %s with true definition from dylib %s\n", existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); break; case Options::kCommonsConflictsDylibsError: @@ -2486,6 +3349,9 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) 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; @@ -2497,12 +3363,12 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) switch ( fOwner.fOptions.commonsMode() ) { case Options::kCommonsIgnoreDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", + fprintf(stderr, "ld: using common symbol %s from %s and ignoring defintion from dylib %s\n", newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); break; case Options::kCommonsOverriddenByDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing defintion of %s from dylib %s with common symbol from %s\n", + fprintf(stderr, "ld: replacing defintion of %s from dylib %s with common symbol from %s\n", newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); useNew = false; break; @@ -2517,6 +3383,9 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // 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; @@ -2528,12 +3397,12 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) switch ( fOwner.fOptions.commonsMode() ) { case Options::kCommonsIgnoreDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: using common symbol %s from %s and ignoring defintion from dylib %s\n", + fprintf(stderr, "ld: using common symbol %s from %s and ignoring defintion from dylib %s\n", newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); break; case Options::kCommonsOverriddenByDylibs: if ( fOwner.fOptions.warnCommons() ) - fprintf(stderr, "ld64: replacing defintion of %s from dylib %s with common symbol from %s\n", + fprintf(stderr, "ld: replacing defintion of %s from dylib %s with common symbol from %s\n", newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); useNew = false; break; @@ -2549,8 +3418,36 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) // 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\n", 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\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); + break; } } + if ( (existingAtom != NULL) && (newAtom.getScope() != existingAtom->getScope()) ) { + fprintf(stderr, "ld: warning %s has different visibility (%d) in %s and (%d) in %s\n", + newAtom.getDisplayName(), newAtom.getScope(), newAtom.getFile()->getPath(), existingAtom->getScope(), existingAtom->getFile()->getPath()); + } if ( useNew ) { fTable[name] = &newAtom; if ( existingAtom != NULL ) @@ -2586,22 +3483,53 @@ void Linker::SymbolTable::getNeededNames(bool andWeakDefintions, std::vectorgetSection()->getIndex(); unsigned int rightSectionIndex = right->getSection()->getIndex(); if ( leftSectionIndex != rightSectionIndex) return (leftSectionIndex < rightSectionIndex); - // then sort by .o file order - ObjectFile::Reader* leftReader = left->getFile(); - ObjectFile::Reader* rightReader = right->getFile(); - if ( leftReader != rightReader ) - return leftReader->getSortOrder() < rightReader->getSortOrder(); - - // lastly sort by atom within a .o file - return left->getSortOrder() < right->getSortOrder(); + // 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(); } @@ -2629,13 +3557,12 @@ int main(int argc, const char* argv[]) ld.link(); } catch (const char* msg) { - extern const double ld64VersionNumber; if ( archInferred ) - fprintf(stderr, "ld64-%g failed: %s for inferred architecture %s\n", ld64VersionNumber, msg, archName); + fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); else if ( showArch ) - fprintf(stderr, "ld64-%g failed: %s for architecture %s\n", ld64VersionNumber, msg, archName); + fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); else - fprintf(stderr, "ld64-%g failed: %s\n", ld64VersionNumber, msg); + fprintf(stderr, "ld: %s\n", msg); return 1; } diff --git a/src/machochecker.cpp b/src/machochecker.cpp index 1d7aec5..7f0e134 100644 --- a/src/machochecker.cpp +++ b/src/machochecker.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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -37,6 +37,8 @@ #include #include +#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -71,11 +73,20 @@ private: 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); @@ -90,13 +101,13 @@ private: 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; - pint_t fRelocBase; bool fWriteableSegmentWithAddrOver4G; const macho_segment_command

* fFirstSegment; const macho_segment_command

* fFirstWritableSegment; @@ -185,9 +196,9 @@ template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) - : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fIndirectTableCount(0), + : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0), fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0), - fRelocBase(0), fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL) + fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL) { // sanity check if ( ! validFile(fileContent) ) @@ -205,6 +216,8 @@ MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, c checkIndirectSymbolTable(); checkRelocations(); + + checkSymbolTable(); } @@ -215,10 +228,11 @@ void MachOChecker::checkMachHeader() throw "sizeofcmds in mach_header is larger than file"; uint32_t flags = fHeader->flags(); - uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFFC0000; + const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFE00000; 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 @@ -250,12 +264,18 @@ void MachOChecker::checkLoadCommands() case LC_ID_DYLINKER: case macho_routines_command

::CMD: case LC_SUB_FRAMEWORK: - case LC_SUB_UMBRELLA: case LC_SUB_CLIENT: case LC_TWOLEVEL_HINTS: case LC_PREBIND_CKSUM: case LC_LOAD_WEAK_DYLIB: case LC_UUID: + case LC_REEXPORT_DYLIB: + case LC_SEGMENT_SPLIT_INFO: + 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()); @@ -324,9 +344,13 @@ void MachOChecker::checkLoadCommands() // cache interesting segments if ( fFirstSegment == NULL ) fFirstSegment = segCmd; - if ( (fFirstWritableSegment == NULL) && ((segCmd->initprot() & VM_PROT_WRITE) != 0) ) - fFirstWritableSegment = 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()]; @@ -372,7 +396,7 @@ void MachOChecker::checkLoadCommands() } } - // check LC_SYMTAB and LC_DYSYMTAB + // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO cmd = cmds; bool foundDynamicSymTab = false; for (uint32_t i = 0; i < cmd_count; ++i) { @@ -386,12 +410,18 @@ void MachOChecker::checkLoadCommands() 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: @@ -399,33 +429,54 @@ void MachOChecker::checkLoadCommands() if ( isStaticExecutable ) throw "LC_DYSYMTAB should not be used in static executable"; foundDynamicSymTab = true; - const macho_dysymtab_command

* dsymtab = (struct macho_dysymtab_command

*)cmd; - fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); - fIndirectTableCount = dsymtab->nindirectsyms(); + fDynamicSymbolTable = (struct macho_dysymtab_command

*)cmd; + fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff()); + fIndirectTableCount = fDynamicSymbolTable->nindirectsyms(); if ( fIndirectTableCount != 0 ) { - if ( dsymtab->indirectsymoff() < linkEditSegment->fileoff() ) + if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() ) throw "indirect symbol table not in __LINKEDIT"; - if ( (dsymtab->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + 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 = dsymtab->nlocrel(); + fLocalRelocationsCount = fDynamicSymbolTable->nlocrel(); if ( fLocalRelocationsCount != 0 ) { - fLocalRelocations = (const macho_relocation_info

*)((char*)fHeader + dsymtab->locreloff()); - if ( dsymtab->locreloff() < linkEditSegment->fileoff() ) + fLocalRelocations = (const macho_relocation_info

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

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + 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 = dsymtab->nextrel(); + fExternalRelocationsCount = fDynamicSymbolTable->nextrel(); if ( fExternalRelocationsCount != 0 ) { - fExternalRelocations = (const macho_relocation_info

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

)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) - throw "local relocations not in __LINKEDIT"; + 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()); } @@ -433,9 +484,7 @@ void MachOChecker::checkLoadCommands() throw "missing dynamic symbol table"; if ( fStrings == NULL ) throw "missing symbol table"; - - fRelocBase = this->relocBase(); - + } template @@ -490,6 +539,23 @@ void MachOChecker::checkIndirectSymbolTable() } +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() @@ -549,7 +615,17 @@ bool MachOChecker::addressInWritableSegment(pint_t address) template <> void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) { - // FIX + 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 <> @@ -564,14 +640,24 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* if ( reloc->r_extern() == 0 ) throw "local relocation found with external relocations"; if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) ) - throw "local relocation address not in writable segment"; + throw "external relocation address not in writable segment"; // FIX: check r_symbol } template <> void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) { - // FIX + 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 } @@ -649,9 +735,19 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* r 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]; @@ -679,33 +775,38 @@ static void check(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 < fh->nfat_arch; ++i) { - if ( archs[i].cputype == CPU_TYPE_POWERPC ) { - if ( MachOChecker::validFile(p + archs[i].offset) ) - MachOChecker::make(p + archs[i].offset, archs[i].size, path); + 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"; - } - else if ( archs[i].cputype == CPU_TYPE_I386 ) { - if ( MachOChecker::validFile(p + archs[i].offset) ) - MachOChecker::make(p + archs[i].offset, archs[i].size, path); + 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"; - } - else if ( archs[i].cputype == CPU_TYPE_POWERPC64 ) { - if ( MachOChecker::validFile(p + archs[i].offset) ) - MachOChecker::make(p + archs[i].offset, archs[i].size, path); + 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"; - } - else if ( archs[i].cputype == CPU_TYPE_X86_64 ) { - if ( MachOChecker::validFile(p + archs[i].offset) ) - MachOChecker::make(p + archs[i].offset, archs[i].size, path); + 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"; - } - else { - throw "in universal file, unknown architecture slice"; + break; + default: + throwf("in universal file, unknown architecture slice 0x%x\n", cputype); } } } diff --git a/unit-tests/bin/exit-non-zero-pass.pl b/unit-tests/bin/exit-non-zero-pass.pl index dd4c3e5..fcc65eb 100755 --- a/unit-tests/bin/exit-non-zero-pass.pl +++ b/unit-tests/bin/exit-non-zero-pass.pl @@ -9,8 +9,13 @@ 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 == system(@ARGV)) +if(0 == $exit_value || 0 != $crashed) { printf("FAIL $string\n"); } @@ -18,5 +23,5 @@ else { printf("PASS $string\n"); } -exit 0; +exit 0; diff --git a/unit-tests/bin/fail-if-exit-non-zero.pl b/unit-tests/bin/fail-if-exit-non-zero.pl index 24ca895..7fb54b2 100755 --- a/unit-tests/bin/fail-if-exit-non-zero.pl +++ b/unit-tests/bin/fail-if-exit-non-zero.pl @@ -10,12 +10,7 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { if(system(@ARGV) != 0) { printf("FAIL $test_name\n"); - exit 1; + exit 1; } -else -{ - exit 0; -} - - +exit 0; diff --git a/unit-tests/bin/fail-if-exit-zero.pl b/unit-tests/bin/fail-if-exit-zero.pl index f1610c9..7859888 100755 --- a/unit-tests/bin/fail-if-exit-zero.pl +++ b/unit-tests/bin/fail-if-exit-zero.pl @@ -7,12 +7,16 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { $test_name = $ENV{UNIT_TEST_NAME}; } -if(system(@ARGV) == 0) +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; } -else -{ - exit 0; -} + +exit 0; diff --git a/unit-tests/bin/fail-if-no-stdin.pl b/unit-tests/bin/fail-if-no-stdin.pl index 0c8ae66..a44f5d3 100755 --- a/unit-tests/bin/fail-if-no-stdin.pl +++ b/unit-tests/bin/fail-if-no-stdin.pl @@ -16,5 +16,7 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { if( eof STDIN ) { printf("FAIL $test_name\n"); + exit 1; } + exit 0; diff --git a/unit-tests/bin/fail-if-stdin.pl b/unit-tests/bin/fail-if-stdin.pl index f5e2fdf..c0bf290 100755 --- a/unit-tests/bin/fail-if-stdin.pl +++ b/unit-tests/bin/fail-if-stdin.pl @@ -15,10 +15,8 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { if( eof STDIN ) { - -} -else -{ - printf("FAIL $test_name\n"); + exit 0; } -exit 0; + +printf("FAIL $test_name\n"); +exit 1; diff --git a/unit-tests/bin/fail-iff-exit-zero.pl b/unit-tests/bin/fail-iff-exit-zero.pl index f62b797..6d49a25 100755 --- a/unit-tests/bin/fail-iff-exit-zero.pl +++ b/unit-tests/bin/fail-iff-exit-zero.pl @@ -13,13 +13,17 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { $test_name = $ENV{UNIT_TEST_NAME}; } -if(0 == system(@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 $test_name\n"); + exit 1; } -else -{ - printf("PASS $test_name\n"); -} -exit 0; +printf("PASS $test_name\n"); +exit 0; diff --git a/unit-tests/bin/make-recursive-newtest.pl b/unit-tests/bin/make-recursive-newtest.pl new file mode 100755 index 0000000..031a45d --- /dev/null +++ b/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/unit-tests/bin/make-recursive.pl b/unit-tests/bin/make-recursive.pl index a441350..f860985 100755 --- a/unit-tests/bin/make-recursive.pl +++ b/unit-tests/bin/make-recursive.pl @@ -7,6 +7,9 @@ use Cwd qw(realpath); my @args = @ARGV; +$ENV{'LD_NO_CLASSSIC_LINKER'} = '1'; +$ENV{'LD_NO_CLASSSIC_LINKER_STATIC'} = '1'; + my $makefiles = { 'makefile' => undef, diff --git a/unit-tests/bin/mkld b/unit-tests/bin/mkld new file mode 100755 index 0000000..33aacc1 --- /dev/null +++ b/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/unit-tests/bin/pass-iff-exit-non-zero.pl b/unit-tests/bin/pass-iff-exit-non-zero.pl new file mode 100755 index 0000000..beb76a9 --- /dev/null +++ b/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/unit-tests/bin/pass-iff-exit-zero.pl b/unit-tests/bin/pass-iff-exit-zero.pl index ff0f1ed..07854b5 100755 --- a/unit-tests/bin/pass-iff-exit-zero.pl +++ b/unit-tests/bin/pass-iff-exit-zero.pl @@ -13,13 +13,11 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { $test_name = $ENV{UNIT_TEST_NAME}; } -if(0 == system(@ARGV)) -{ - printf("PASS $test_name\n"); -} -else +if(0 != system(@ARGV)) { printf("FAIL $test_name\n"); + exit 1; } -exit 0; +printf("PASS $test_name\n"); +exit 0; diff --git a/unit-tests/bin/pass-iff-no-stdin.pl b/unit-tests/bin/pass-iff-no-stdin.pl index 08903f6..19b98b4 100755 --- a/unit-tests/bin/pass-iff-no-stdin.pl +++ b/unit-tests/bin/pass-iff-no-stdin.pl @@ -16,10 +16,8 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { if( eof STDIN ) { printf("PASS $test_name\n"); + exit 0; } -else -{ - printf("FAIL $test_name\n"); -} -exit 0; +printf("FAIL $test_name\n"); +exit 1; diff --git a/unit-tests/bin/pass-iff-stdin.pl b/unit-tests/bin/pass-iff-stdin.pl index 0b56925..d5ee99f 100755 --- a/unit-tests/bin/pass-iff-stdin.pl +++ b/unit-tests/bin/pass-iff-stdin.pl @@ -16,10 +16,9 @@ if ( exists $ENV{UNIT_TEST_NAME} ) { if( eof STDIN ) { printf("FAIL $test_name\n"); + exit 1 } -else -{ - printf("PASS $test_name\n"); -} + +printf("PASS $test_name\n"); exit 0; diff --git a/unit-tests/bin/result-filter.pl b/unit-tests/bin/result-filter.pl index 9443452..8168e79 100755 --- a/unit-tests/bin/result-filter.pl +++ b/unit-tests/bin/result-filter.pl @@ -110,12 +110,16 @@ sub process_entry { if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) { - printf "%-40s %s\n", $test_name, $line; $total_count++; if($line =~ m/^PASS.+/) { $pass_count++; } + else + { + # only print failure lines + printf "%-40s %s\n", $test_name, $line; + } $seen_result = 1; } } diff --git a/unit-tests/bin/rm-stale-test-logs b/unit-tests/bin/rm-stale-test-logs new file mode 100755 index 0000000..936dec1 --- /dev/null +++ b/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/unit-tests/clean-tests b/unit-tests/clean-tests new file mode 100755 index 0000000..4c38b4e --- /dev/null +++ b/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/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 266eb85..8462385 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -6,25 +6,25 @@ SHELL = /bin/sh ARCH ?= $(shell arch) # set default to be all -VALID_ARCHS ?= "ppc ppc64 i386" +VALID_ARCHS ?= "ppc ppc64 i386 x86_64" + +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}:${PATH} + PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH} +else + PATH := ${MYDIR}:${PATH}: + COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: endif +export PATH +export COMPILER_PATH LD = ld OBJECTDUMP = ObjectDump MACHOCHECK = machocheck - OTOOL = otool -ifeq (${ARCH},ppc64) -OTOOL = otool64 -endif -ifeq (${ARCH},x86_64) -OTOOL = otool64 -endif - CC = gcc-4.0 -arch ${ARCH} CCFLAGS = -Wall -std=c99 @@ -37,12 +37,17 @@ RM = rm RMFLAGS = -rf # utilites for Makefiles -PASS_IFF = ${TESTROOT}/bin/pass-iff-exit-zero.pl -PASS_IFF_EMPTY = ${TESTROOT}/bin/pass-iff-no-stdin.pl -PASS_IFF_STDIN = ${TESTROOT}/bin/pass-iff-stdin.pl -PASS_IFF_GOOD_MACHO = ${TESTROOT}/bin/pass-iff-exit-zero.pl ${MACHOCHECK} -FAIL_IFF = ${TESTROOT}/bin/fail-iff-exit-zero.pl -FAIL_IF_BAD_MACHO = ${TESTROOT}/bin/fail-if-exit-non-zero.pl ${MACHOCHECK} -FAIL_IF_SUCCESS = ${TESTROOT}/bin/fail-if-exit-zero.pl -FAIL_IF_EMPTY = ${TESTROOT}/bin/fail-if-no-stdin.pl -FAIL_IF_STDIN = ${TESTROOT}/bin/fail-if-stdin.pl +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/unit-tests/proctor-run b/unit-tests/proctor-run new file mode 100755 index 0000000..e7bdea4 --- /dev/null +++ b/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/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index 3756bd1..945df48 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -3,21 +3,23 @@ # 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 - ../bin/make-recursive.pl ARCH=$arch VALID_ARCHS="$all_archs" | ../bin/result-filter.pl + [ "$NEWTEST" ] && NT=-newtest + + ../bin/make-recursive$NT.pl ARCH=$arch VALID_ARCHS="$all_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/unit-tests/run-all-unit-tests-debug b/unit-tests/run-all-unit-tests-debug new file mode 100755 index 0000000..a239748 --- /dev/null +++ b/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/unit-tests/src/Makefile b/unit-tests/src/Makefile new file mode 100644 index 0000000..d3bdeab --- /dev/null +++ b/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/unit-tests/src/results-to-xml.cpp b/unit-tests/src/results-to-xml.cpp new file mode 100644 index 0000000..a305347 --- /dev/null +++ b/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/unit-tests/src/xmlparser/xmlparser.1 b/unit-tests/src/xmlparser/xmlparser.1 new file mode 100644 index 0000000..6eab06a --- /dev/null +++ b/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/unit-tests/src/xmlparser/xmlparser.m b/unit-tests/src/xmlparser/xmlparser.m new file mode 100644 index 0000000..a9c82fc --- /dev/null +++ b/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/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj b/unit-tests/src/xmlparser/xmlparser.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9492293 --- /dev/null +++ b/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/unit-tests/src/xmlparser/xmlparser_Prefix.pch b/unit-tests/src/xmlparser/xmlparser_Prefix.pch new file mode 100644 index 0000000..530e057 --- /dev/null +++ b/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/unit-tests/test-cases/16-byte-alignment/Makefile b/unit-tests/test-cases/16-byte-alignment/Makefile new file mode 100644 index 0000000..a3a256f --- /dev/null +++ b/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/unit-tests/test-cases/16-byte-alignment/comment.txt b/unit-tests/test-cases/16-byte-alignment/comment.txt new file mode 100644 index 0000000..eb9eaf5 --- /dev/null +++ b/unit-tests/test-cases/16-byte-alignment/comment.txt @@ -0,0 +1 @@ +Test 16 byte alignment with -O2 optimization. Radar #4662185 diff --git a/unit-tests/test-cases/16-byte-alignment/tl_test2.c b/unit-tests/test-cases/16-byte-alignment/tl_test2.c new file mode 100644 index 0000000..ff27fec --- /dev/null +++ b/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/unit-tests/test-cases/absolute-symbol/Makefile b/unit-tests/test-cases/absolute-symbol/Makefile new file mode 100644 index 0000000..065b6f3 --- /dev/null +++ b/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 + ${LD} -arch ${ARCH} -static -r main.o abs.o -o all.o + nm -m all.o | grep _myAbs | grep absolute | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -static all.o -e _main -o main2 + ${PASS_IFF_GOOD_MACHO} main2 + +clean: + rm -rf main.o abs.o all.o main main2 diff --git a/unit-tests/test-cases/absolute-symbol/abs.s b/unit-tests/test-cases/absolute-symbol/abs.s new file mode 100644 index 0000000..216867c --- /dev/null +++ b/unit-tests/test-cases/absolute-symbol/abs.s @@ -0,0 +1,3 @@ + + .globl _myAbs + .set _myAbs, 0xfe000000 diff --git a/unit-tests/test-cases/absolute-symbol/main.c b/unit-tests/test-cases/absolute-symbol/main.c new file mode 100644 index 0000000..eec3f61 --- /dev/null +++ b/unit-tests/test-cases/absolute-symbol/main.c @@ -0,0 +1,5 @@ + +extern int* myAbs; + +int main() { return *myAbs; } + diff --git a/unit-tests/test-cases/alias-command-line/Makefile b/unit-tests/test-cases/alias-command-line/Makefile new file mode 100644 index 0000000..9e87329 --- /dev/null +++ b/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/unit-tests/test-cases/alias-command-line/aliases.s b/unit-tests/test-cases/alias-command-line/aliases.s new file mode 100644 index 0000000..ffab4a9 --- /dev/null +++ b/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/unit-tests/test-cases/alias-command-line/aliases.txt b/unit-tests/test-cases/alias-command-line/aliases.txt new file mode 100644 index 0000000..291f2f7 --- /dev/null +++ b/unit-tests/test-cases/alias-command-line/aliases.txt @@ -0,0 +1,6 @@ +_foo _fooalt +# comment +_foo _fooalt2 + + + diff --git a/unit-tests/test-cases/alias-objects/Makefile b/unit-tests/test-cases/alias-objects/Makefile new file mode 100644 index 0000000..f4bfdc8 --- /dev/null +++ b/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/unit-tests/test-cases/alias-objects/aliases.s b/unit-tests/test-cases/alias-objects/aliases.s new file mode 100644 index 0000000..b21f8c7 --- /dev/null +++ b/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/unit-tests/test-cases/align-modulus/Makefile b/unit-tests/test-cases/align-modulus/Makefile index bf79c29..6b6a50f 100644 --- a/unit-tests/test-cases/align-modulus/Makefile +++ b/unit-tests/test-cases/align-modulus/Makefile @@ -33,11 +33,8 @@ 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 foo.${ARCH}.dylib - - - + rm -rf *.dylib diff --git a/unit-tests/test-cases/align-modulus/comment.txt b/unit-tests/test-cases/align-modulus/comment.txt new file mode 100644 index 0000000..240c171 --- /dev/null +++ b/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/unit-tests/test-cases/allow-stack-execute/Makefile b/unit-tests/test-cases/allow-stack-execute/Makefile index 9c2aaba..6e0be3f 100644 --- a/unit-tests/test-cases/allow-stack-execute/Makefile +++ b/unit-tests/test-cases/allow-stack-execute/Makefile @@ -43,4 +43,4 @@ all: ${OTOOL} -hv foo-${ARCH} | grep ALLOW_STACK_EXECUTION | ${PASS_IFF_EMPTY} clean: - rm -rf foo-${ARCH} + rm -rf foo-* diff --git a/unit-tests/test-cases/allow-stack-execute/comment.txt b/unit-tests/test-cases/allow-stack-execute/comment.txt new file mode 100644 index 0000000..525ab2b --- /dev/null +++ b/unit-tests/test-cases/allow-stack-execute/comment.txt @@ -0,0 +1 @@ +Test the we set the stack execution bit properly. diff --git a/unit-tests/test-cases/allowable-client/Makefile b/unit-tests/test-cases/allowable-client/Makefile index 88e215b..ef293a6 100644 --- a/unit-tests/test-cases/allowable-client/Makefile +++ b/unit-tests/test-cases/allowable-client/Makefile @@ -46,16 +46,21 @@ all: # 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 + ${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 + ${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 @@ -66,6 +71,11 @@ all: ${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 @@ -75,16 +85,26 @@ all: ${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 + ${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} + ${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 *.${ARCH}.dylib *.${ARCH}.bundle main.${ARCH} fail.log foo.framework bar.framework main.dylib + rm -rf *.dylib *.bundle main.???* fail.log *.framework diff --git a/unit-tests/test-cases/allowable-client/comment.txt b/unit-tests/test-cases/allowable-client/comment.txt new file mode 100644 index 0000000..0202b5f --- /dev/null +++ b/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/unit-tests/test-cases/archive-ObjC/Makefile b/unit-tests/test-cases/archive-ObjC/Makefile new file mode 100644 index 0000000..f5e0e24 --- /dev/null +++ b/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 + ${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/unit-tests/test-cases/archive-ObjC/bar.c b/unit-tests/test-cases/archive-ObjC/bar.c new file mode 100644 index 0000000..d9b468b --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC/bar.c @@ -0,0 +1,2 @@ + +int bar() { return 0; } diff --git a/unit-tests/test-cases/archive-ObjC/baz.m b/unit-tests/test-cases/archive-ObjC/baz.m new file mode 100644 index 0000000..90ae0a1 --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC/baz.m @@ -0,0 +1,8 @@ +#include + +@interface Baz : NSObject +@end + +@implementation Baz +@end + diff --git a/unit-tests/test-cases/archive-ObjC/foo.m b/unit-tests/test-cases/archive-ObjC/foo.m new file mode 100644 index 0000000..acba7a4 --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC/foo.m @@ -0,0 +1,8 @@ +#include + +@interface Foo : NSObject +@end + +@implementation Foo +@end + diff --git a/unit-tests/test-cases/archive-ObjC/main.c b/unit-tests/test-cases/archive-ObjC/main.c new file mode 100644 index 0000000..dc2fbef --- /dev/null +++ b/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/unit-tests/test-cases/archive-basic/Makefile b/unit-tests/test-cases/archive-basic/Makefile index 721cf2a..39c0ef9 100644 --- a/unit-tests/test-cases/archive-basic/Makefile +++ b/unit-tests/test-cases/archive-basic/Makefile @@ -32,7 +32,9 @@ 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} @@ -41,6 +43,4 @@ all: ${FAIL_IF_BAD_MACHO} main-${ARCH} clean: - rm -rf main-${ARCH} foo-${ARCH}.o bar-${ARCH}.o libfoobar-${ARCH}.a - - + rm -rf main-* *.o *.a diff --git a/unit-tests/test-cases/archive-basic/comment.txt b/unit-tests/test-cases/archive-basic/comment.txt new file mode 100644 index 0000000..0d34170 --- /dev/null +++ b/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/unit-tests/test-cases/archive-duplicate/Makefile b/unit-tests/test-cases/archive-duplicate/Makefile new file mode 100644 index 0000000..61f4bc7 --- /dev/null +++ b/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/unit-tests/test-cases/archive-duplicate/bar.c b/unit-tests/test-cases/archive-duplicate/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/unit-tests/test-cases/archive-duplicate/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/unit-tests/test-cases/archive-duplicate/foo.c b/unit-tests/test-cases/archive-duplicate/foo.c new file mode 100644 index 0000000..a60f28c --- /dev/null +++ b/unit-tests/test-cases/archive-duplicate/foo.c @@ -0,0 +1 @@ +int foo() { return 1; } diff --git a/unit-tests/test-cases/archive-duplicate/main.c b/unit-tests/test-cases/archive-duplicate/main.c new file mode 100644 index 0000000..57c4c68 --- /dev/null +++ b/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/unit-tests/test-cases/archive-weak/Makefile b/unit-tests/test-cases/archive-weak/Makefile index 676d1a4..2d53734 100644 --- a/unit-tests/test-cases/archive-weak/Makefile +++ b/unit-tests/test-cases/archive-weak/Makefile @@ -37,14 +37,15 @@ 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-${ARCH} foo-${ARCH}.o bar-${ARCH}.o baz-${ARCH}.o libfoobar-${ARCH}.a - - + rm -rf main-* *.o *.a diff --git a/unit-tests/test-cases/archive-weak/comment.txt b/unit-tests/test-cases/archive-weak/comment.txt new file mode 100644 index 0000000..902d22c --- /dev/null +++ b/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/unit-tests/test-cases/auto-arch/Makefile b/unit-tests/test-cases/auto-arch/Makefile index e5caa64..3c8b3e5 100644 --- a/unit-tests/test-cases/auto-arch/Makefile +++ b/unit-tests/test-cases/auto-arch/Makefile @@ -35,11 +35,10 @@ run: all all: ${CC} ${CCFLAGS} hello.c -c -o hello.o -mmacosx-version-min=10.4 - ${LD} -lcrt1.o hello.o -o hello -lSystem 2> fail.log - ${FAIL_IF_BAD_MACHO} hello - file hello | grep ${ARCH} | ${PASS_IFF_STDIN} + ${FAIL_IF_BAD_OBJ} hello.o + ${LD} -r -lcrt1.o hello.o -o hello-r.o -lSystem + ${FAIL_IF_ERROR} ${OBJECTDUMP} hello-r.o >/dev/null + file hello-r.o | grep ${ARCH} | ${PASS_IFF_STDIN} clean: - rm hello.o hello fail.log - - + rm -rf *.o diff --git a/unit-tests/test-cases/auto-arch2/Makefile b/unit-tests/test-cases/auto-arch2/Makefile new file mode 100644 index 0000000..a6f2520 --- /dev/null +++ b/unit-tests/test-cases/auto-arch2/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 + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is a check that ld +# can figure out which architecture is needed +# by looking at the .o files when -arch is not used. +# + +run: all + + +all: + ${CC} ${CCFLAGS} hello.c -c -o hello-${ARCH}.o -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_OBJ} hello-${ARCH}.o + ${FAIL_IF_ERROR} ${LD} -r -lcrt1.o hello-${ARCH}.o -o hello-r.o -lSystem + ${FAIL_IF_ERROR} ObjectDump hello-r.o >/dev/null + file hello-r.o | grep ${ARCH} | ${PASS_IFF_STDIN} + +clean: + rm -f *.o diff --git a/unit-tests/test-cases/auto-arch2/comment.txt b/unit-tests/test-cases/auto-arch2/comment.txt new file mode 100644 index 0000000..ed107a9 --- /dev/null +++ b/unit-tests/test-cases/auto-arch2/comment.txt @@ -0,0 +1,3 @@ +The point of this test is a check that ld +can figure out which architecture is needed +by looking at the .o files when -arch is not used. diff --git a/unit-tests/test-cases/auto-arch2/hello.c b/unit-tests/test-cases/auto-arch2/hello.c new file mode 100644 index 0000000..14d9363 --- /dev/null +++ b/unit-tests/test-cases/auto-arch2/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/unit-tests/test-cases/blank-stubs/Makefile b/unit-tests/test-cases/blank-stubs/Makefile index 514fd8b..b99e085 100644 --- a/unit-tests/test-cases/blank-stubs/Makefile +++ b/unit-tests/test-cases/blank-stubs/Makefile @@ -34,8 +34,24 @@ run: all all: # build example fully fat dylib - ${CC} `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib - lipo libfoo.dylib -remove ${ARCH} -output 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 + 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} @@ -43,5 +59,4 @@ all: clean: - rm -rf libfoo.dylib main - \ No newline at end of file + rm -rf *.dylib main diff --git a/unit-tests/test-cases/blank-stubs/comment.txt b/unit-tests/test-cases/blank-stubs/comment.txt new file mode 100644 index 0000000..77a8764 --- /dev/null +++ b/unit-tests/test-cases/blank-stubs/comment.txt @@ -0,0 +1 @@ +Test that blank stubs are handled properly diff --git a/unit-tests/test-cases/bundle_loader/Makefile b/unit-tests/test-cases/bundle_loader/Makefile index 3b31968..c0163d0 100644 --- a/unit-tests/test-cases/bundle_loader/Makefile +++ b/unit-tests/test-cases/bundle_loader/Makefile @@ -32,12 +32,12 @@ run: all all: ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib ${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} clean: - rm libbar.dylib main bundle.bundle - - + rm *.dylib main bundle.bundle diff --git a/unit-tests/test-cases/bundle_loader/comment.txt b/unit-tests/test-cases/bundle_loader/comment.txt new file mode 100644 index 0000000..e406042 --- /dev/null +++ b/unit-tests/test-cases/bundle_loader/comment.txt @@ -0,0 +1,3 @@ +The point of this test is a sanity check that ld +-bundle_loader works, and _bar is found in the executable +and not in libbar.dylib diff --git a/unit-tests/test-cases/commons-alignment/Makefile b/unit-tests/test-cases/commons-alignment/Makefile new file mode 100644 index 0000000..d073c0e --- /dev/null +++ b/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/unit-tests/test-cases/commons-alignment/foo.s b/unit-tests/test-cases/commons-alignment/foo.s new file mode 100644 index 0000000..03c28fc --- /dev/null +++ b/unit-tests/test-cases/commons-alignment/foo.s @@ -0,0 +1,2 @@ + + .comm _mycomm64aligned,15,6 diff --git a/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile b/unit-tests/test-cases/commons-coalesced-dead_strip/Makefile new file mode 100644 index 0000000..5823a61 --- /dev/null +++ b/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/unit-tests/test-cases/commons-coalesced-dead_strip/a.c b/unit-tests/test-cases/commons-coalesced-dead_strip/a.c new file mode 100644 index 0000000..fea7234 --- /dev/null +++ b/unit-tests/test-cases/commons-coalesced-dead_strip/a.c @@ -0,0 +1,4 @@ +#include "c.h" + +float aa() { return bar; } + diff --git a/unit-tests/test-cases/commons-coalesced-dead_strip/b.c b/unit-tests/test-cases/commons-coalesced-dead_strip/b.c new file mode 100644 index 0000000..12e46e8 --- /dev/null +++ b/unit-tests/test-cases/commons-coalesced-dead_strip/b.c @@ -0,0 +1,4 @@ +#include "c.h" + +float bb() { return bar; } + diff --git a/unit-tests/test-cases/commons-coalesced-dead_strip/c.c b/unit-tests/test-cases/commons-coalesced-dead_strip/c.c new file mode 100644 index 0000000..165d5e4 --- /dev/null +++ b/unit-tests/test-cases/commons-coalesced-dead_strip/c.c @@ -0,0 +1,3 @@ + +const float bar = 1.0; + diff --git a/unit-tests/test-cases/commons-coalesced-dead_strip/c.h b/unit-tests/test-cases/commons-coalesced-dead_strip/c.h new file mode 100644 index 0000000..86a61ae --- /dev/null +++ b/unit-tests/test-cases/commons-coalesced-dead_strip/c.h @@ -0,0 +1,4 @@ + +// missing extern +const float bar; + diff --git a/unit-tests/test-cases/commons-mixed/Makefile b/unit-tests/test-cases/commons-mixed/Makefile new file mode 100644 index 0000000..a59a858 --- /dev/null +++ b/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/unit-tests/test-cases/commons-mixed/bar.c b/unit-tests/test-cases/commons-mixed/bar.c new file mode 100644 index 0000000..771802c --- /dev/null +++ b/unit-tests/test-cases/commons-mixed/bar.c @@ -0,0 +1,2 @@ + +int bar; diff --git a/unit-tests/test-cases/commons-mixed/foo.c b/unit-tests/test-cases/commons-mixed/foo.c new file mode 100644 index 0000000..42e0a8e --- /dev/null +++ b/unit-tests/test-cases/commons-mixed/foo.c @@ -0,0 +1,2 @@ + +int foo; diff --git a/unit-tests/test-cases/commons-order/Makefile b/unit-tests/test-cases/commons-order/Makefile new file mode 100644 index 0000000..68e282d --- /dev/null +++ b/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} 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/unit-tests/test-cases/commons-order/bar.c b/unit-tests/test-cases/commons-order/bar.c new file mode 100644 index 0000000..2d258fc --- /dev/null +++ b/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/unit-tests/test-cases/commons-order/baz.c b/unit-tests/test-cases/commons-order/baz.c new file mode 100644 index 0000000..6497d29 --- /dev/null +++ b/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/unit-tests/test-cases/commons-order/expected.order b/unit-tests/test-cases/commons-order/expected.order new file mode 100644 index 0000000..4738fc2 --- /dev/null +++ b/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/unit-tests/test-cases/commons-order/foo.c b/unit-tests/test-cases/commons-order/foo.c new file mode 100644 index 0000000..ca80a59 --- /dev/null +++ b/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/unit-tests/test-cases/commons-order/main.c b/unit-tests/test-cases/commons-order/main.c new file mode 100644 index 0000000..df77448 --- /dev/null +++ b/unit-tests/test-cases/commons-order/main.c @@ -0,0 +1,4 @@ + + +int main() { return 0; } + diff --git a/unit-tests/test-cases/cpu-sub-types/Makefile b/unit-tests/test-cases/cpu-sub-types/Makefile new file mode 100644 index 0000000..50f05e9 --- /dev/null +++ b/unit-tests/test-cases/cpu-sub-types/Makefile @@ -0,0 +1,91 @@ +## +# 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 validate cpu subtypes processing +# + +test: test-${ARCH} + +test-ppc64: + ${PASS_IFF} true + +test-i386: + ${PASS_IFF} true + +test-x86_64: + ${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 -mmacosx-version-min=10.4 -c -o foo-G3.o + ${FAIL_IF_BAD_OBJ} foo-G3.o + gcc foo.c -arch ppc7400 -mmacosx-version-min=10.4 -c -o foo-G4.o + ${FAIL_IF_BAD_OBJ} foo-G4.o + gcc foo.c -arch ppc970 -mmacosx-version-min=10.4 -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 -mmacosx-version-min=10.4 -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/unit-tests/test-cases/cpu-sub-types/comment.txt b/unit-tests/test-cases/cpu-sub-types/comment.txt new file mode 100644 index 0000000..5e47ece --- /dev/null +++ b/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/unit-tests/test-cases/cpu-sub-types/foo.c b/unit-tests/test-cases/cpu-sub-types/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/cpu-sub-types/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/cpu-sub-types/main.c b/unit-tests/test-cases/cpu-sub-types/main.c new file mode 100644 index 0000000..b48076d --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip-archive-global/Makefile b/unit-tests/test-cases/dead_strip-archive-global/Makefile new file mode 100644 index 0000000..ef985b7 --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip-archive-global/foo.c b/unit-tests/test-cases/dead_strip-archive-global/foo.c new file mode 100644 index 0000000..41478e8 --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip-archive-global/main.c b/unit-tests/test-cases/dead_strip-archive-global/main.c new file mode 100644 index 0000000..da2b2fd --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip-archive/Makefile b/unit-tests/test-cases/dead_strip-archive/Makefile index c2e66c0..1542d78 100644 --- a/unit-tests/test-cases/dead_strip-archive/Makefile +++ b/unit-tests/test-cases/dead_strip-archive/Makefile @@ -24,20 +24,20 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# The point of this test that -dead_strip removes unreference code/data from archives +# 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 - ${FAIL_IF_BAD_MACHO} main - nm -j main | grep dead_wood | ${PASS_IFF_EMPTY} - + ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf main libfoo.a foo.o - - + rm -rf main *.a *.o diff --git a/unit-tests/test-cases/dead_strip-archive/comment.txt b/unit-tests/test-cases/dead_strip-archive/comment.txt new file mode 100644 index 0000000..8dde1c5 --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip/Makefile b/unit-tests/test-cases/dead_strip/Makefile index 5985e96..32ac1c5 100644 --- a/unit-tests/test-cases/dead_strip/Makefile +++ b/unit-tests/test-cases/dead_strip/Makefile @@ -44,9 +44,6 @@ all: ${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-${ARCH} dylib-${ARCH} dylib2-${ARCH} - - + rm -rf main-* dylib-* dylib2-* diff --git a/unit-tests/test-cases/dead_strip/comment.txt b/unit-tests/test-cases/dead_strip/comment.txt new file mode 100644 index 0000000..90a289c --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip_dylibs/Makefile b/unit-tests/test-cases/dead_strip_dylibs/Makefile new file mode 100644 index 0000000..309655e --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip_dylibs/bar.c b/unit-tests/test-cases/dead_strip_dylibs/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/unit-tests/test-cases/dead_strip_dylibs/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/dead_strip_dylibs/baz.c b/unit-tests/test-cases/dead_strip_dylibs/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/unit-tests/test-cases/dead_strip_dylibs/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/dead_strip_dylibs/foo.c b/unit-tests/test-cases/dead_strip_dylibs/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/dead_strip_dylibs/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/dead_strip_dylibs/main.c b/unit-tests/test-cases/dead_strip_dylibs/main.c new file mode 100644 index 0000000..2b85b0e --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip_section_attribute/Makefile b/unit-tests/test-cases/dead_strip_section_attribute/Makefile new file mode 100644 index 0000000..e6471f7 --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip_section_attribute/comment.txt b/unit-tests/test-cases/dead_strip_section_attribute/comment.txt new file mode 100644 index 0000000..6ca56e4 --- /dev/null +++ b/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/unit-tests/test-cases/dead_strip_section_attribute/main.c b/unit-tests/test-cases/dead_strip_section_attribute/main.c new file mode 100644 index 0000000..5fc1fad --- /dev/null +++ b/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/unit-tests/test-cases/dtrace-static-probes/Makefile b/unit-tests/test-cases/dtrace-static-probes/Makefile new file mode 100644 index 0000000..5046ea1 --- /dev/null +++ b/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} main.c -o main + +main-dead_strip: main.c foo.h bar.h + ${CC} main.c -o main-dead_strip -dead_strip + +main-r: main.c foo.h bar.h + ${CC} 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} main-r.o -o main-r + +libmain.dylib: main.c foo.h bar.h + ${CC} 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 main main-r main-dead_strip foo.h bar.h *.o diff --git a/unit-tests/test-cases/dtrace-static-probes/bar.d b/unit-tests/test-cases/dtrace-static-probes/bar.d new file mode 100644 index 0000000..fab36ea --- /dev/null +++ b/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/unit-tests/test-cases/dtrace-static-probes/comment.txt b/unit-tests/test-cases/dtrace-static-probes/comment.txt new file mode 100644 index 0000000..b02ac8b --- /dev/null +++ b/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/unit-tests/test-cases/dtrace-static-probes/foo.d b/unit-tests/test-cases/dtrace-static-probes/foo.d new file mode 100644 index 0000000..03f1a19 --- /dev/null +++ b/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/unit-tests/test-cases/dtrace-static-probes/main.c b/unit-tests/test-cases/dtrace-static-probes/main.c new file mode 100644 index 0000000..bbce55f --- /dev/null +++ b/unit-tests/test-cases/dtrace-static-probes/main.c @@ -0,0 +1,28 @@ + +#include + +typedef int weirdType; +typedef int weirdType2; + +#include "foo.h" +#include "bar.h" + + +int deadwood() +{ + BAR_COUNT1(2); +} + + +int main() { + int a = 1; + + while(a) { + FOO_COUNT1(1); + printf("test\n"); + BAR_COUNT1(2); + sleep(1); + } + + return 0; +} diff --git a/unit-tests/test-cases/dwarf-archive-all_load/Makefile b/unit-tests/test-cases/dwarf-archive-all_load/Makefile new file mode 100644 index 0000000..8d38d99 --- /dev/null +++ b/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/unit-tests/test-cases/dwarf-archive-all_load/bar.c b/unit-tests/test-cases/dwarf-archive-all_load/bar.c new file mode 100644 index 0000000..06752f3 --- /dev/null +++ b/unit-tests/test-cases/dwarf-archive-all_load/bar.c @@ -0,0 +1,2 @@ +void bar() {} + diff --git a/unit-tests/test-cases/dwarf-archive-all_load/baz.c b/unit-tests/test-cases/dwarf-archive-all_load/baz.c new file mode 100644 index 0000000..256a0e3 --- /dev/null +++ b/unit-tests/test-cases/dwarf-archive-all_load/baz.c @@ -0,0 +1 @@ +void baz() {} diff --git a/unit-tests/test-cases/dwarf-archive-all_load/comment.txt b/unit-tests/test-cases/dwarf-archive-all_load/comment.txt new file mode 100644 index 0000000..6992acd --- /dev/null +++ b/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/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs b/unit-tests/test-cases/dwarf-archive-all_load/expected-stabs new file mode 100644 index 0000000..0071455 --- /dev/null +++ b/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/unit-tests/test-cases/dwarf-archive-all_load/foo.c b/unit-tests/test-cases/dwarf-archive-all_load/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/unit-tests/test-cases/dwarf-archive-all_load/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl b/unit-tests/test-cases/dwarf-archive-all_load/stabs-filter.pl new file mode 100755 index 0000000..706fd12 --- /dev/null +++ b/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/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile index 8159cd9..1eb6310 100644 --- a/unit-tests/test-cases/dwarf-debug-notes-r/Makefile +++ b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile @@ -33,29 +33,27 @@ include ${TESTROOT}/include/common.makefile run: all -all: foobar.o main.o crt1.o - ${CXX} ${CCXXFLAGS} foobar.o main.o -o dwarf-test-${ARCH} -L. +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 foo.o -mdynamic-no-pic + ${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 bar.o -mdynamic-no-pic + ${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 main.o -mdynamic-no-pic - -# don't want any stabs in crt1.o to effect output, so my private stripped copy -crt1.o : /usr/lib/crt1.o - strip -S /usr/lib/crt1.o -o crt1.o + ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ clean: - rm -rf dwarf-test-${ARCH} foo.o bar.o foobar.o main.o crt1.o dwarf-test-${ARCH}.stabs - - + rm -rf dwarf-test-* *.o *.stabs diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt b/unit-tests/test-cases/dwarf-debug-notes-r/comment.txt new file mode 100644 index 0000000..0f1d0b1 --- /dev/null +++ b/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/unit-tests/test-cases/dwarf-debug-notes/Makefile b/unit-tests/test-cases/dwarf-debug-notes/Makefile index 8b94b2f..42f1cb7 100644 --- a/unit-tests/test-cases/dwarf-debug-notes/Makefile +++ b/unit-tests/test-cases/dwarf-debug-notes/Makefile @@ -32,23 +32,19 @@ include ${TESTROOT}/include/common.makefile run: all -all: hello.o other.o crt1.o - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o dwarf-hello-${ARCH} -L. +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 hello.o -mdynamic-no-pic + ${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 other.o -mdynamic-no-pic - -# don't want any stabs in crt1.o to effect output, so my private stripped copy -crt1.o : /usr/lib/crt1.o - strip -S /usr/lib/crt1.o -o crt1.o + ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o $@ -mdynamic-no-pic + ${FAIL_IF_BAD_OBJ} $@ clean: - rm -rf dwarf-hello-${ARCH} hello.o other.o crt1.o dwarf-hello-${ARCH}.stabs - - + rm -rf dwarf-hello-* *.o *.stabs diff --git a/unit-tests/test-cases/dwarf-debug-notes/comment.txt b/unit-tests/test-cases/dwarf-debug-notes/comment.txt new file mode 100644 index 0000000..5caf129 --- /dev/null +++ b/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/unit-tests/test-cases/dwarf-ignore/Makefile b/unit-tests/test-cases/dwarf-ignore/Makefile index 773e572..94b026c 100644 --- a/unit-tests/test-cases/dwarf-ignore/Makefile +++ b/unit-tests/test-cases/dwarf-ignore/Makefile @@ -36,6 +36,4 @@ all: size -l dwarf-hello-${ARCH} | grep __DWARF | ${PASS_IFF_EMPTY} clean: - rm -rf dwarf-hello-${ARCH} - - + rm -rf dwarf-hello-* diff --git a/unit-tests/test-cases/dwarf-ignore/comment.txt b/unit-tests/test-cases/dwarf-ignore/comment.txt new file mode 100644 index 0000000..06822b2 --- /dev/null +++ b/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/unit-tests/test-cases/dwarf-strip/Makefile b/unit-tests/test-cases/dwarf-strip/Makefile index 916eff1..947afa7 100644 --- a/unit-tests/test-cases/dwarf-strip/Makefile +++ b/unit-tests/test-cases/dwarf-strip/Makefile @@ -32,10 +32,9 @@ 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} + nm -ap dwarf-hello-${ARCH} | grep -e " - " | ${PASS_IFF_EMPTY} clean: - rm -rf dwarf-hello-${ARCH} - - + rm -rf dwarf-hello-* diff --git a/unit-tests/test-cases/dwarf-strip/comment.txt b/unit-tests/test-cases/dwarf-strip/comment.txt new file mode 100644 index 0000000..5a23827 --- /dev/null +++ b/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/unit-tests/test-cases/dylib-aliases/Makefile b/unit-tests/test-cases/dylib-aliases/Makefile new file mode 100644 index 0000000..5c34470 --- /dev/null +++ b/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} foo.c -dynamiclib -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} 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/unit-tests/test-cases/dylib-aliases/bar.c b/unit-tests/test-cases/dylib-aliases/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/unit-tests/test-cases/dylib-aliases/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/unit-tests/test-cases/dylib-aliases/foo.c b/unit-tests/test-cases/dylib-aliases/foo.c new file mode 100644 index 0000000..2fb55ee --- /dev/null +++ b/unit-tests/test-cases/dylib-aliases/foo.c @@ -0,0 +1 @@ +void foo() { } diff --git a/unit-tests/test-cases/dylib-aliases/main.c b/unit-tests/test-cases/dylib-aliases/main.c new file mode 100644 index 0000000..03c4b39 --- /dev/null +++ b/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/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/unit-tests/test-cases/dylib-re-export-cycle/Makefile new file mode 100644 index 0000000..617fbfc --- /dev/null +++ b/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/unit-tests/test-cases/dylib-re-export-cycle/bar.c b/unit-tests/test-cases/dylib-re-export-cycle/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/unit-tests/test-cases/dylib-re-export-cycle/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/unit-tests/test-cases/dylib-re-export-cycle/foo.c b/unit-tests/test-cases/dylib-re-export-cycle/foo.c new file mode 100644 index 0000000..2fb55ee --- /dev/null +++ b/unit-tests/test-cases/dylib-re-export-cycle/foo.c @@ -0,0 +1 @@ +void foo() { } diff --git a/unit-tests/test-cases/dylib-re-export-cycle/main.c b/unit-tests/test-cases/dylib-re-export-cycle/main.c new file mode 100644 index 0000000..8273541 --- /dev/null +++ b/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/unit-tests/test-cases/dylib_file-missing/Makefile b/unit-tests/test-cases/dylib_file-missing/Makefile new file mode 100644 index 0000000..f6371af --- /dev/null +++ b/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/unit-tests/test-cases/dylib_file-missing/bar.c b/unit-tests/test-cases/dylib_file-missing/bar.c new file mode 100644 index 0000000..2668f9a --- /dev/null +++ b/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/unit-tests/test-cases/dylib_file-missing/foo.c b/unit-tests/test-cases/dylib_file-missing/foo.c new file mode 100644 index 0000000..aa31241 --- /dev/null +++ b/unit-tests/test-cases/dylib_file-missing/foo.c @@ -0,0 +1,7 @@ + +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/unit-tests/test-cases/dylib_file-missing/main.c b/unit-tests/test-cases/dylib_file-missing/main.c new file mode 100644 index 0000000..3f109f2 --- /dev/null +++ b/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/unit-tests/test-cases/dylib_file/Makefile b/unit-tests/test-cases/dylib_file/Makefile new file mode 100644 index 0000000..181eee5 --- /dev/null +++ b/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/unit-tests/test-cases/dylib_file/bar.c b/unit-tests/test-cases/dylib_file/bar.c new file mode 100644 index 0000000..2668f9a --- /dev/null +++ b/unit-tests/test-cases/dylib_file/bar.c @@ -0,0 +1,13 @@ + + + +void bar() +{ +} + +#if BAR_EXTRA +void bar_extra() +{ +} +#endif + diff --git a/unit-tests/test-cases/dylib_file/comment.txt b/unit-tests/test-cases/dylib_file/comment.txt new file mode 100644 index 0000000..e3bfbb7 --- /dev/null +++ b/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/unit-tests/test-cases/dylib_file/foo.c b/unit-tests/test-cases/dylib_file/foo.c new file mode 100644 index 0000000..aa31241 --- /dev/null +++ b/unit-tests/test-cases/dylib_file/foo.c @@ -0,0 +1,7 @@ + +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/unit-tests/test-cases/dylib_file/main.c b/unit-tests/test-cases/dylib_file/main.c new file mode 100644 index 0000000..3f109f2 --- /dev/null +++ b/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/unit-tests/test-cases/dylib_init/Makefile b/unit-tests/test-cases/dylib_init/Makefile new file mode 100644 index 0000000..89db531 --- /dev/null +++ b/unit-tests/test-cases/dylib_init/Makefile @@ -0,0 +1,41 @@ +## +# 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 + +# When creating a dylib with the -init option to specify the init routine in 64-bit, +# libtool should not complain "file not found: __init". + +run: all + +all: + ${FAIL_IF_ERROR} ${CC} -m64 -c foo.c -o foo_64.o + ${FAIL_IF_BAD_OBJ} foo_64.o + ${FAIL_IF_ERROR} libtool -dynamic -o libtest.A.dylib foo_64.o \ + -init __init \ + -compatibility_version 1 -current_version 1 -install_name "/build/ld64_problem/libtest.A.dylib" \ + -lSystem + ${PASS_IFF_GOOD_MACHO} libtest.A.dylib + +clean: + rm -rf *.dylib *.o diff --git a/unit-tests/test-cases/dylib_init/comment.txt b/unit-tests/test-cases/dylib_init/comment.txt new file mode 100644 index 0000000..9355f2c --- /dev/null +++ b/unit-tests/test-cases/dylib_init/comment.txt @@ -0,0 +1,2 @@ +When creating a dylib with the -init option to specify the init routine in 64-bit, +libtool sould not complain "file not found: __init". diff --git a/unit-tests/test-cases/dylib_init/foo.c b/unit-tests/test-cases/dylib_init/foo.c new file mode 100644 index 0000000..9ba156e --- /dev/null +++ b/unit-tests/test-cases/dylib_init/foo.c @@ -0,0 +1,2 @@ +void _init() { +} diff --git a/unit-tests/test-cases/eh-strip-test/Makefile b/unit-tests/test-cases/eh-strip-test/Makefile new file mode 100644 index 0000000..0b8fcff --- /dev/null +++ b/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/unit-tests/test-cases/eh-strip-test/comment.txt b/unit-tests/test-cases/eh-strip-test/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/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/unit-tests/test-cases/eh-strip-test/main.cxx b/unit-tests/test-cases/eh-strip-test/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/unit-tests/test-cases/eh-strip-test/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/unit-tests/test-cases/eh_frame/Makefile b/unit-tests/test-cases/eh_frame/Makefile new file mode 100644 index 0000000..6216d8e --- /dev/null +++ b/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/unit-tests/test-cases/eh_frame/bar.cxx b/unit-tests/test-cases/eh_frame/bar.cxx new file mode 100644 index 0000000..9623da1 --- /dev/null +++ b/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/unit-tests/test-cases/eh_frame/foo.cxx b/unit-tests/test-cases/eh_frame/foo.cxx new file mode 100644 index 0000000..d1e2dd1 --- /dev/null +++ b/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/unit-tests/test-cases/end-label/Makefile b/unit-tests/test-cases/end-label/Makefile new file mode 100644 index 0000000..69f51a7 --- /dev/null +++ b/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/unit-tests/test-cases/end-label/bar.s b/unit-tests/test-cases/end-label/bar.s new file mode 100644 index 0000000..db389c4 --- /dev/null +++ b/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/unit-tests/test-cases/end-label/foo.s b/unit-tests/test-cases/end-label/foo.s new file mode 100644 index 0000000..888af5f --- /dev/null +++ b/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/unit-tests/test-cases/exported-symbols-wildcards/Makefile b/unit-tests/test-cases/exported-symbols-wildcards/Makefile new file mode 100644 index 0000000..1890820 --- /dev/null +++ b/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} -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} -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} -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} -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} -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} -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} -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} -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} -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} -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} -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/unit-tests/test-cases/exported-symbols-wildcards/expect1 b/unit-tests/test-cases/exported-symbols-wildcards/expect1 new file mode 100644 index 0000000..75aadb3 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect1 @@ -0,0 +1,2 @@ +_foo2bar +_foobar diff --git a/unit-tests/test-cases/exported-symbols-wildcards/expect2 b/unit-tests/test-cases/exported-symbols-wildcards/expect2 new file mode 100644 index 0000000..09883f8 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect2 @@ -0,0 +1,3 @@ +_fao +_ffo +_foo diff --git a/unit-tests/test-cases/exported-symbols-wildcards/expect3 b/unit-tests/test-cases/exported-symbols-wildcards/expect3 new file mode 100644 index 0000000..478bf69 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect3 @@ -0,0 +1,4 @@ +_foo +_foo2 +_foo2bar +_foobar diff --git a/unit-tests/test-cases/exported-symbols-wildcards/expect4 b/unit-tests/test-cases/exported-symbols-wildcards/expect4 new file mode 100644 index 0000000..b78c206 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect4 @@ -0,0 +1,6 @@ +_fao +_ffo +_foo +_foo2 +_foo2bar +_foobar diff --git a/unit-tests/test-cases/exported-symbols-wildcards/expect5 b/unit-tests/test-cases/exported-symbols-wildcards/expect5 new file mode 100644 index 0000000..cc935a4 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect5 @@ -0,0 +1,3 @@ +_foo +_foo2bar +_foobar diff --git a/unit-tests/test-cases/exported-symbols-wildcards/expect6 b/unit-tests/test-cases/exported-symbols-wildcards/expect6 new file mode 100644 index 0000000..bdf1d31 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect6 @@ -0,0 +1,4 @@ +_fao +_ffo +_foo +_foobar diff --git a/unit-tests/test-cases/exported-symbols-wildcards/expect7 b/unit-tests/test-cases/exported-symbols-wildcards/expect7 new file mode 100644 index 0000000..c691c40 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect7 @@ -0,0 +1,2 @@ +_fao +_ffo diff --git a/unit-tests/test-cases/exported-symbols-wildcards/expect8 b/unit-tests/test-cases/exported-symbols-wildcards/expect8 new file mode 100644 index 0000000..09883f8 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/expect8 @@ -0,0 +1,3 @@ +_fao +_ffo +_foo diff --git a/unit-tests/test-cases/exported-symbols-wildcards/foo.c b/unit-tests/test-cases/exported-symbols-wildcards/foo.c new file mode 100644 index 0000000..bc5be8e --- /dev/null +++ b/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/unit-tests/test-cases/exported-symbols-wildcards/list5 b/unit-tests/test-cases/exported-symbols-wildcards/list5 new file mode 100644 index 0000000..a6776d2 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-wildcards/list5 @@ -0,0 +1,2 @@ +_foo +_*bar diff --git a/unit-tests/test-cases/exported_symbols_list-eol/Makefile b/unit-tests/test-cases/exported_symbols_list-eol/Makefile new file mode 100644 index 0000000..25a50b4 --- /dev/null +++ b/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/unit-tests/test-cases/exported_symbols_list-eol/expected.nm b/unit-tests/test-cases/exported_symbols_list-eol/expected.nm new file mode 100644 index 0000000..b21cbbf --- /dev/null +++ b/unit-tests/test-cases/exported_symbols_list-eol/expected.nm @@ -0,0 +1,2 @@ +_common_global2 +_func_global2 diff --git a/unit-tests/test-cases/exported_symbols_list-eol/test.c b/unit-tests/test-cases/exported_symbols_list-eol/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/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/unit-tests/test-cases/exported_symbols_list-eol/test.exp b/unit-tests/test-cases/exported_symbols_list-eol/test.exp new file mode 100644 index 0000000..6ab1532 --- /dev/null +++ b/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/unit-tests/test-cases/exported_symbols_list-r/Makefile b/unit-tests/test-cases/exported_symbols_list-r/Makefile new file mode 100644 index 0000000..96ef763 --- /dev/null +++ b/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/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp b/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp new file mode 100644 index 0000000..73154ba --- /dev/null +++ b/unit-tests/test-cases/exported_symbols_list-r/test-bad.exp @@ -0,0 +1,3 @@ +_bar +_baz +_foobar diff --git a/unit-tests/test-cases/exported_symbols_list-r/test.c b/unit-tests/test-cases/exported_symbols_list-r/test.c new file mode 100644 index 0000000..c9fdc35 --- /dev/null +++ b/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/unit-tests/test-cases/exported_symbols_list-r/test.exp b/unit-tests/test-cases/exported_symbols_list-r/test.exp new file mode 100644 index 0000000..93e7e17 --- /dev/null +++ b/unit-tests/test-cases/exported_symbols_list-r/test.exp @@ -0,0 +1,2 @@ +_func_global2 +_common_global2 diff --git a/unit-tests/test-cases/external-reloc-sorting/Makefile b/unit-tests/test-cases/external-reloc-sorting/Makefile new file mode 100644 index 0000000..d9b92d5 --- /dev/null +++ b/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/unit-tests/test-cases/external-reloc-sorting/foo.c b/unit-tests/test-cases/external-reloc-sorting/foo.c new file mode 100644 index 0000000..bc28725 --- /dev/null +++ b/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/unit-tests/test-cases/external-reloc-sorting/main.c b/unit-tests/test-cases/external-reloc-sorting/main.c new file mode 100644 index 0000000..6c68c4c --- /dev/null +++ b/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/unit-tests/test-cases/filelist/Makefile b/unit-tests/test-cases/filelist/Makefile index 78a1004..0b1a743 100644 --- a/unit-tests/test-cases/filelist/Makefile +++ b/unit-tests/test-cases/filelist/Makefile @@ -27,7 +27,7 @@ include ${TESTROOT}/include/common.makefile PWD = $(shell pwd) # -# The point of this test is to check the two forms of the' +# The point of this test is to check the two forms of the # -filelist option # @@ -35,14 +35,13 @@ 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} -arch ${ARCH} -filelist "${PWD}/filelist1" -o "${PWD}/hello-${ARCH}" + 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} -arch ${ARCH} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}" + cd /tmp && ${CC} ${CCFLAGS} -filelist "${PWD}/filelist2,${PWD}" -o "${PWD}/hello-${ARCH}" ${PASS_IFF_GOOD_MACHO} hello-${ARCH} clean: - rm hello-${ARCH} hello-${ARCH}.o filelist1 filelist2 - - + rm hello-* *.o filelist1 filelist2 diff --git a/unit-tests/test-cases/filelist/comment.txt b/unit-tests/test-cases/filelist/comment.txt new file mode 100644 index 0000000..f193b11 --- /dev/null +++ b/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/unit-tests/test-cases/flat-dylib/Makefile b/unit-tests/test-cases/flat-dylib/Makefile new file mode 100644 index 0000000..b1015b2 --- /dev/null +++ b/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/unit-tests/test-cases/flat-dylib/main.c b/unit-tests/test-cases/flat-dylib/main.c new file mode 100644 index 0000000..6014829 --- /dev/null +++ b/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/unit-tests/test-cases/flat-indirect-undefines/Makefile b/unit-tests/test-cases/flat-indirect-undefines/Makefile new file mode 100644 index 0000000..a3715e0 --- /dev/null +++ b/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/unit-tests/test-cases/flat-indirect-undefines/bar.c b/unit-tests/test-cases/flat-indirect-undefines/bar.c new file mode 100644 index 0000000..b348aa8 --- /dev/null +++ b/unit-tests/test-cases/flat-indirect-undefines/bar.c @@ -0,0 +1,4 @@ + +void bar() {} + + diff --git a/unit-tests/test-cases/flat-indirect-undefines/foo.c b/unit-tests/test-cases/flat-indirect-undefines/foo.c new file mode 100644 index 0000000..39df2ea --- /dev/null +++ b/unit-tests/test-cases/flat-indirect-undefines/foo.c @@ -0,0 +1,8 @@ + +extern void bar(); + +void foo() +{ + bar(); +} + diff --git a/unit-tests/test-cases/flat-indirect-undefines/main.c b/unit-tests/test-cases/flat-indirect-undefines/main.c new file mode 100644 index 0000000..246fed4 --- /dev/null +++ b/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/unit-tests/test-cases/flat-main/Makefile b/unit-tests/test-cases/flat-main/Makefile new file mode 100644 index 0000000..d31b7ab --- /dev/null +++ b/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/unit-tests/test-cases/flat-main/main.c b/unit-tests/test-cases/flat-main/main.c new file mode 100644 index 0000000..6014829 --- /dev/null +++ b/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/unit-tests/test-cases/got-elimination/Makefile b/unit-tests/test-cases/got-elimination/Makefile new file mode 100644 index 0000000..e856b71 --- /dev/null +++ b/unit-tests/test-cases/got-elimination/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 + +# +# Check that ld can remove non-lazy pointers for x86_64 +# + +all: all-${ARCH} + +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/unit-tests/test-cases/got-elimination/bar.c b/unit-tests/test-cases/got-elimination/bar.c new file mode 100644 index 0000000..781c6fd --- /dev/null +++ b/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/unit-tests/test-cases/got-elimination/foo.c b/unit-tests/test-cases/got-elimination/foo.c new file mode 100644 index 0000000..45675a3 --- /dev/null +++ b/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/unit-tests/test-cases/header-pad/Makefile b/unit-tests/test-cases/header-pad/Makefile index e4393cd..d8cdd51 100644 --- a/unit-tests/test-cases/header-pad/Makefile +++ b/unit-tests/test-cases/header-pad/Makefile @@ -35,6 +35,4 @@ all: ${PASS_IFF_GOOD_MACHO} hello-${ARCH} clean: - rm hello-${ARCH} - - + rm hello-* diff --git a/unit-tests/test-cases/header-pad/comment.txt b/unit-tests/test-cases/header-pad/comment.txt new file mode 100644 index 0000000..79114eb --- /dev/null +++ b/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/unit-tests/test-cases/hello-world/Makefile b/unit-tests/test-cases/hello-world/Makefile index 5ce56cc..a1ade02 100644 --- a/unit-tests/test-cases/hello-world/Makefile +++ b/unit-tests/test-cases/hello-world/Makefile @@ -35,6 +35,4 @@ all: ${PASS_IFF_GOOD_MACHO} hello-${ARCH} clean: - rm hello-${ARCH} - - + rm hello-* diff --git a/unit-tests/test-cases/hello-world/comment.txt b/unit-tests/test-cases/hello-world/comment.txt new file mode 100644 index 0000000..79114eb --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common2/Makefile.newtest b/unit-tests/test-cases/implicit-common2/Makefile.newtest new file mode 100644 index 0000000..af93da1 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common2/a.c b/unit-tests/test-cases/implicit-common2/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common2/comment.txt b/unit-tests/test-cases/implicit-common2/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common2/test.c b/unit-tests/test-cases/implicit-common2/test.c new file mode 100644 index 0000000..94578c6 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common3/Makefile b/unit-tests/test-cases/implicit-common3/Makefile new file mode 100644 index 0000000..caed2d4 --- /dev/null +++ b/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} ${LDFLAGS} a-${ARCH}.o -L. -ltest-${ARCH} -o a-${ARCH} + ${PASS_IFF_GOOD_MACHO} a-${ARCH} + +clean: + rm -rf *.o *.a a-* diff --git a/unit-tests/test-cases/implicit-common3/a.c b/unit-tests/test-cases/implicit-common3/a.c new file mode 100644 index 0000000..110842f --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common3/comment.txt b/unit-tests/test-cases/implicit-common3/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common3/test.c b/unit-tests/test-cases/implicit-common3/test.c new file mode 100644 index 0000000..e0fd7e8 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common4/Makefile.newtest b/unit-tests/test-cases/implicit-common4/Makefile.newtest new file mode 100644 index 0000000..d70c634 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common4/a.c b/unit-tests/test-cases/implicit-common4/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common4/comment.txt b/unit-tests/test-cases/implicit-common4/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common4/test.c b/unit-tests/test-cases/implicit-common4/test.c new file mode 100644 index 0000000..94578c6 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common5/Makefile.newtest b/unit-tests/test-cases/implicit-common5/Makefile.newtest new file mode 100644 index 0000000..d5b6d73 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common5/a.c b/unit-tests/test-cases/implicit-common5/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common5/comment.txt b/unit-tests/test-cases/implicit-common5/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/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/unit-tests/test-cases/implicit-common5/test.c b/unit-tests/test-cases/implicit-common5/test.c new file mode 100644 index 0000000..f34267a --- /dev/null +++ b/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/unit-tests/test-cases/indirect-dylib/Makefile b/unit-tests/test-cases/indirect-dylib/Makefile index 32cb276..e3056a5 100644 --- a/unit-tests/test-cases/indirect-dylib/Makefile +++ b/unit-tests/test-cases/indirect-dylib/Makefile @@ -36,11 +36,11 @@ 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 libfoo.dylib libbar.dylib main fail.log - - + rm *.dylib main fail.log diff --git a/unit-tests/test-cases/indirect-dylib/comment.txt b/unit-tests/test-cases/indirect-dylib/comment.txt new file mode 100644 index 0000000..311aa79 --- /dev/null +++ b/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/unit-tests/test-cases/indirect-path-search/Makefile b/unit-tests/test-cases/indirect-path-search/Makefile new file mode 100644 index 0000000..440dd08 --- /dev/null +++ b/unit-tests/test-cases/indirect-path-search/Makefile @@ -0,0 +1,91 @@ +## +# 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. + ${PASS_IFF_GOOD_MACHO} main + + + + + +clean: + + rm -rf hide libbar.dylib libfoo.dylib Foo.framework Bar.framework main diff --git a/unit-tests/test-cases/indirect-path-search/bar.c b/unit-tests/test-cases/indirect-path-search/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/unit-tests/test-cases/indirect-path-search/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/indirect-path-search/baz.c b/unit-tests/test-cases/indirect-path-search/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/unit-tests/test-cases/indirect-path-search/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/indirect-path-search/foo.c b/unit-tests/test-cases/indirect-path-search/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/indirect-path-search/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/indirect-path-search/main.c b/unit-tests/test-cases/indirect-path-search/main.c new file mode 100644 index 0000000..f02701a --- /dev/null +++ b/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/unit-tests/test-cases/large-data/Makefile b/unit-tests/test-cases/large-data/Makefile new file mode 100644 index 0000000..408a2da --- /dev/null +++ b/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/unit-tests/test-cases/large-data/test1.c b/unit-tests/test-cases/large-data/test1.c new file mode 100644 index 0000000..2d9ec94 --- /dev/null +++ b/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/unit-tests/test-cases/large-data/test2.c b/unit-tests/test-cases/large-data/test2.c new file mode 100644 index 0000000..d97bed1 --- /dev/null +++ b/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/unit-tests/test-cases/large-data/test3.c b/unit-tests/test-cases/large-data/test3.c new file mode 100644 index 0000000..b7ca398 --- /dev/null +++ b/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/unit-tests/test-cases/large-data/test4.c b/unit-tests/test-cases/large-data/test4.c new file mode 100644 index 0000000..d879c5c --- /dev/null +++ b/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/unit-tests/test-cases/late-link-error/Makefile b/unit-tests/test-cases/late-link-error/Makefile index 209d5a3..e3a0d65 100644 --- a/unit-tests/test-cases/late-link-error/Makefile +++ b/unit-tests/test-cases/late-link-error/Makefile @@ -38,6 +38,4 @@ all: ${FAIL_IFF} cat link_error-${ARCH} 2> fail.log clean: - rm link_error-${ARCH} fail.log - - + rm link_error-* fail.log diff --git a/unit-tests/test-cases/late-link-error/comment.txt b/unit-tests/test-cases/late-link-error/comment.txt new file mode 100644 index 0000000..716686d --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment/Makefile b/unit-tests/test-cases/literals-coalesce-alignment/Makefile index b27351b..15f3307 100644 --- a/unit-tests/test-cases/literals-coalesce-alignment/Makefile +++ b/unit-tests/test-cases/literals-coalesce-alignment/Makefile @@ -32,14 +32,15 @@ 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 - ${OBJECTDUMP} cstring-align-3-${ARCH}.o | grep 'align:' > align-3 - ${OBJECTDUMP} cstring-r-${ARCH}.o | grep 'align:' > align-r - ${PASS_IFF} diff align-3 align-r + ${FAIL_IF_ERROR} ${OBJECTDUMP} cstring-r-${ARCH}.o | grep 'align:' > align-r -clean: - rm -rf cstring-align-0-${ARCH}.o cstring-align-3-${ARCH}.o cstring-r-${ARCH}.o align-3 align-r - - + ${PASS_IFF} diff align-3 align-r +clean: + rm -rf *.o align-3 align-r diff --git a/unit-tests/test-cases/literals-coalesce-alignment2/Makefile b/unit-tests/test-cases/literals-coalesce-alignment2/Makefile new file mode 100644 index 0000000..c3c8c80 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt b/unit-tests/test-cases/literals-coalesce-alignment2/comment.txt new file mode 100644 index 0000000..3b2e3c7 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s b/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-0.s new file mode 100644 index 0000000..1a55cf6 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s b/unit-tests/test-cases/literals-coalesce-alignment2/cstring-align-3.s new file mode 100644 index 0000000..211aa72 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment3/Makefile b/unit-tests/test-cases/literals-coalesce-alignment3/Makefile new file mode 100644 index 0000000..cac20b9 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt b/unit-tests/test-cases/literals-coalesce-alignment3/comment.txt new file mode 100644 index 0000000..3b2e3c7 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s b/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-0.s new file mode 100644 index 0000000..1a55cf6 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s b/unit-tests/test-cases/literals-coalesce-alignment3/cstring-align-3.s new file mode 100644 index 0000000..211aa72 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce/Makefile b/unit-tests/test-cases/literals-coalesce/Makefile index 80465f8..dcf1dbe 100644 --- a/unit-tests/test-cases/literals-coalesce/Makefile +++ b/unit-tests/test-cases/literals-coalesce/Makefile @@ -32,12 +32,9 @@ run: all all: ${CC} ${ASMFLAGS} literals.s -c -o literals-${ARCH}.o - ${OBJECTDUMP} literals-${ARCH}.o | grep 'name:'| uniq -c | grep -v '^ [1|2]' | ${FAIL_IF_STDIN} + ${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 - ${OBJECTDUMP} literals-r-${ARCH}.o | grep 'name:' | uniq -d | ${PASS_IFF_EMPTY} + ${FAIL_IF_ERROR} ${OBJECTDUMP} literals-r-${ARCH}.o | grep 'name:' | uniq -d | ${PASS_IFF_EMPTY} clean: - rm -rf literals-${ARCH}.o -o literals-r-${ARCH}.o - - - + rm -rf *.o diff --git a/unit-tests/test-cases/literals-coalesce2/Makefile.newtest b/unit-tests/test-cases/literals-coalesce2/Makefile.newtest new file mode 100644 index 0000000..0f9d1b5 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce2/comment.txt b/unit-tests/test-cases/literals-coalesce2/comment.txt new file mode 100644 index 0000000..56cea21 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce2/literals.s b/unit-tests/test-cases/literals-coalesce2/literals.s new file mode 100644 index 0000000..b8d4354 --- /dev/null +++ b/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/unit-tests/test-cases/literals-coalesce2/test.sh b/unit-tests/test-cases/literals-coalesce2/test.sh new file mode 100755 index 0000000..57a36b7 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/Makefile b/unit-tests/test-cases/llvm-integration/Makefile new file mode 100644 index 0000000..67e2789 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/Makefile @@ -0,0 +1,229 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile +LLVMGCC = /usr/local/bin/llvm-gcc +LLVMGXX = /usr/local/bin/llvm-g++ +# +# Test the we set the stack execution bit properly. + +run: + @if [ -f /usr/local/bin/llvm-gcc ] ; 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 + + +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-${ARCH}.o ${OTHER} + ${LLVMGCC} ${CCFLAGS} --emit-llvm b.c -c -o b-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main.c -c -o main-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a-${ARCH}.o b-${ARCH}.o main-${ARCH}.o -o main-${ARCH} + ${PASS_IFF_GOOD_MACHO} main-${ARCH} + ${PASS_IFF} ./main-${ARCH} + +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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b1.c -c -o b1-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main1.c -c -o main1-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a1-${ARCH}.o b1-${ARCH}.o main1-${ARCH}.o -o main1-${ARCH} + ${PASS_IFF_GOOD_MACHO} main1-${ARCH} + ${PASS_IFF} ./main1-${ARCH} + +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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b2.c -c -o b2-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main2.c -c -o main2-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a2-${ARCH}.o b2-${ARCH}.o main2-${ARCH}.o -o main2-${ARCH} + ${PASS_IFF_GOOD_MACHO} main2-${ARCH} + ${PASS_IFF} ./main2-${ARCH} + +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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b3.c -c -o b3-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main3.c -c -o main3-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a3-${ARCH}.o b3-${ARCH}.o main3-${ARCH}.o -o main3-${ARCH} + ${PASS_IFF_GOOD_MACHO} main3-${ARCH} + ${PASS_IFF} ./main3-${ARCH} + +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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b4.c -c -o b4-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main4.c -c -o main4-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a4-${ARCH}.o b4-${ARCH}.o main4-${ARCH}.o -o main4-${ARCH} + ${PASS_IFF_GOOD_MACHO} main4-${ARCH} + ${PASS_IFF} ./main4-${ARCH} + +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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b5.c -c -o b5-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main5.c -c -o main5-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a5-${ARCH}.o b5-${ARCH}.o main5-${ARCH}.o -o main5-${ARCH} -Wl,-dead_strip + ${PASS_IFF_GOOD_MACHO} main5-${ARCH} + ${PASS_IFF} ./main5-${ARCH} + ${OTOOL} -tV main5-${ARCH} | grep foo3 | ${PASS_IFF_EMPTY} + +six: + # + # llvm : a6.c : Dfoo1, Dfoo2 + # MachO : main6.c : Ufoo1 + # + #echo "Six..." + ${LLVMGCC} ${CCFLAGS} --emit-llvm a6.c -c -o a6-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main6.c -c -o main6-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a6-${ARCH}.o main6-${ARCH}.o -o main6-${ARCH} -Wl,-dead_strip + ${PASS_IFF_GOOD_MACHO} main6-${ARCH} + ${PASS_IFF} ./main6-${ARCH} + ${OTOOL} -tV main6-${ARCH} | 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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b7.c -c -o b7-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main7.c -c -o main7-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a7-${ARCH}.o b7-${ARCH}.o main7-${ARCH}.o -o main7-${ARCH} + ${PASS_IFF_GOOD_MACHO} main7-${ARCH} + ${PASS_IFF} ./main7-${ARCH} + +eight: + # + # llvm : a8.c : Dfoo1, Dfoo2 + # MachO : main8.c : Ufoo1 + # + #echo "Eight..." + ${LLVMGCC} ${CCFLAGS} --emit-llvm a8.c -c -o a8-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main8.c -c -o main8-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a8-${ARCH}.o main8-${ARCH}.o -o main8-${ARCH} -Wl,-dead_strip + ${PASS_IFF} ./main8-${ARCH} + ${OTOOL} -tV main8-${ARCH} | grep foo2 | ${PASS_IFF_EMPTY} + ${OTOOL} -tV main8-${ARCH} | 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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main9.c -c -o main9-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a9-${ARCH}.o main9-${ARCH}.o -o main9-${ARCH} -Wl,-dead_strip + ${PASS_IFF} ./main9-${ARCH} + ${OTOOL} -tV main9-${ARCH} | grep foo2 | ${PASS_IFF_EMPTY} + ${OTOOL} -tV main9-${ARCH} | grep foo4 | ${PASS_IFF_EMPTY} + ${OTOOL} -tV main9-${ARCH} | 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-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm b10.c -c -o b10-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main10.c -c -o main10-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a10-${ARCH}.o b10-${ARCH}.o main10-${ARCH}.o -o main10-${ARCH} + ${PASS_IFF_GOOD_MACHO} main10-${ARCH} + +eleven: + # + # llvm : a11.c + # MachO : main11.c + # + #echo "Eleven..." + ${LLVMGCC} ${CCFLAGS} --emit-llvm a11.c -c -o a11-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main11.c -c -o main11-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a11-${ARCH}.o main11-${ARCH}.o -o main11-${ARCH} + ${PASS_IFF_GOOD_MACHO} main11-${ARCH} + ./main11-${ARCH} >& /dev/null + +twelve: + # + # llvm : a12.c + # MachO : main12.c + # + #echo "Tweleve..." + ${LLVMGCC} ${CCFLAGS} --emit-llvm a12.c -c -o a12-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} main12.c -c -o main12-${ARCH}.o + ${LLVMGCC} ${CCFLAGS} a12-${ARCH}.o main12-${ARCH}.o -o main12-${ARCH} + ${PASS_IFF_GOOD_MACHO} main12-${ARCH} + ./main12-${ARCH} + +thirteen: + # + # llvm : a13.cc + # MachO : main13.cc + # + # echo "Thirteen..." + ${LLVMGCC} ${CXXFLAGS} --emit-llvm a13.cc -c -o a13-${ARCH}.o + ${LLVMGCC} ${CXXFLAGS} main13.cc -c -o main13-${ARCH}.o + ${LLVMGXX} a13-${ARCH}.o main13-${ARCH}.o -o main13-${ARCH} + ${PASS_IFF_GOOD_MACHO} main13-${ARCH} + + +clean: + rm -rf *.o main*-* big.* diff --git a/unit-tests/test-cases/llvm-integration/a.c b/unit-tests/test-cases/llvm-integration/a.c new file mode 100644 index 0000000..0c96178 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a.c @@ -0,0 +1,5 @@ +int foo3() +{ + return 21; +} + diff --git a/unit-tests/test-cases/llvm-integration/a1.c b/unit-tests/test-cases/llvm-integration/a1.c new file mode 100644 index 0000000..f9fb403 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a10.c b/unit-tests/test-cases/llvm-integration/a10.c new file mode 100644 index 0000000..0dc181e --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a10.c @@ -0,0 +1,5 @@ +extern void foo(void); + +void foo(void) +{ +} diff --git a/unit-tests/test-cases/llvm-integration/a11.c b/unit-tests/test-cases/llvm-integration/a11.c new file mode 100644 index 0000000..e95dc40 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a11.c @@ -0,0 +1,6 @@ +#include +void foo3(void) +{ + fputc ('x', stderr); + printf ("\n"); +} diff --git a/unit-tests/test-cases/llvm-integration/a12.c b/unit-tests/test-cases/llvm-integration/a12.c new file mode 100644 index 0000000..80bc1e8 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a12.h b/unit-tests/test-cases/llvm-integration/a12.h new file mode 100644 index 0000000..be43955 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a13.cc b/unit-tests/test-cases/llvm-integration/a13.cc new file mode 100644 index 0000000..edc938a --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a13.cc @@ -0,0 +1,3 @@ +#include "a13.h" + +A::~A() {} diff --git a/unit-tests/test-cases/llvm-integration/a13.h b/unit-tests/test-cases/llvm-integration/a13.h new file mode 100644 index 0000000..bc00448 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a2.c b/unit-tests/test-cases/llvm-integration/a2.c new file mode 100644 index 0000000..0eb20c5 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a2.c @@ -0,0 +1,6 @@ +extern int foo4(void); +int foo3() +{ + return foo4(); +} + diff --git a/unit-tests/test-cases/llvm-integration/a3.c b/unit-tests/test-cases/llvm-integration/a3.c new file mode 100644 index 0000000..040ca5f --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a3.c @@ -0,0 +1,6 @@ +int bar; +int foo1() +{ + return bar; +} + diff --git a/unit-tests/test-cases/llvm-integration/a4.c b/unit-tests/test-cases/llvm-integration/a4.c new file mode 100644 index 0000000..0eb20c5 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a4.c @@ -0,0 +1,6 @@ +extern int foo4(void); +int foo3() +{ + return foo4(); +} + diff --git a/unit-tests/test-cases/llvm-integration/a5.c b/unit-tests/test-cases/llvm-integration/a5.c new file mode 100644 index 0000000..bcb22d9 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a6.c b/unit-tests/test-cases/llvm-integration/a6.c new file mode 100644 index 0000000..b621453 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a6.c @@ -0,0 +1,10 @@ + +int foo1() +{ + return 42; +} + +int foo2() +{ + return 21; +} diff --git a/unit-tests/test-cases/llvm-integration/a7.c b/unit-tests/test-cases/llvm-integration/a7.c new file mode 100644 index 0000000..560919a --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a8.c b/unit-tests/test-cases/llvm-integration/a8.c new file mode 100644 index 0000000..47352a7 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a9.c b/unit-tests/test-cases/llvm-integration/a9.c new file mode 100644 index 0000000..da2c8fa --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/a9.list b/unit-tests/test-cases/llvm-integration/a9.list new file mode 100644 index 0000000..583bc2f --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/a9.list @@ -0,0 +1,3 @@ +_foo1 +_main +_bar diff --git a/unit-tests/test-cases/llvm-integration/b.c b/unit-tests/test-cases/llvm-integration/b.c new file mode 100644 index 0000000..61c92dc --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/b.c @@ -0,0 +1,3 @@ +int foo2() { + return 21; +} diff --git a/unit-tests/test-cases/llvm-integration/b1.c b/unit-tests/test-cases/llvm-integration/b1.c new file mode 100644 index 0000000..5158abe --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/b1.c @@ -0,0 +1,4 @@ +extern int foo4(); +int foo2() { + return foo4(); +} diff --git a/unit-tests/test-cases/llvm-integration/b10.c b/unit-tests/test-cases/llvm-integration/b10.c new file mode 100644 index 0000000..d899aa9 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/b10.h b/unit-tests/test-cases/llvm-integration/b10.h new file mode 100644 index 0000000..fcb50d2 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/b2.c b/unit-tests/test-cases/llvm-integration/b2.c new file mode 100644 index 0000000..a20f6e3 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/b3.c b/unit-tests/test-cases/llvm-integration/b3.c new file mode 100644 index 0000000..31e7ee8 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/b3.c @@ -0,0 +1,4 @@ +extern int bar; +int foo2() { + return bar; +} diff --git a/unit-tests/test-cases/llvm-integration/b4.c b/unit-tests/test-cases/llvm-integration/b4.c new file mode 100644 index 0000000..9437ad0 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/b5.c b/unit-tests/test-cases/llvm-integration/b5.c new file mode 100644 index 0000000..a105df9 --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/b5.c @@ -0,0 +1,4 @@ +int foo2(void) +{ + return 0; +} diff --git a/unit-tests/test-cases/llvm-integration/b7.c b/unit-tests/test-cases/llvm-integration/b7.c new file mode 100644 index 0000000..d34f91a --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main.c b/unit-tests/test-cases/llvm-integration/main.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main1.c b/unit-tests/test-cases/llvm-integration/main1.c new file mode 100644 index 0000000..ccad10d --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main10.c b/unit-tests/test-cases/llvm-integration/main10.c new file mode 100644 index 0000000..dabcdcf --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main11.c b/unit-tests/test-cases/llvm-integration/main11.c new file mode 100644 index 0000000..82ef7ff --- /dev/null +++ b/unit-tests/test-cases/llvm-integration/main11.c @@ -0,0 +1,7 @@ + +extern void foo3(void); +int main() +{ + foo3(); + return 0; +} diff --git a/unit-tests/test-cases/llvm-integration/main12.c b/unit-tests/test-cases/llvm-integration/main12.c new file mode 100644 index 0000000..4de8725 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main13.cc b/unit-tests/test-cases/llvm-integration/main13.cc new file mode 100644 index 0000000..697d81b --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main2.c b/unit-tests/test-cases/llvm-integration/main2.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main3.c b/unit-tests/test-cases/llvm-integration/main3.c new file mode 100644 index 0000000..a5058fe --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main4.c b/unit-tests/test-cases/llvm-integration/main4.c new file mode 100644 index 0000000..ac424ca --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main5.c b/unit-tests/test-cases/llvm-integration/main5.c new file mode 100644 index 0000000..28d551d --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main6.c b/unit-tests/test-cases/llvm-integration/main6.c new file mode 100644 index 0000000..3d00382 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main7.c b/unit-tests/test-cases/llvm-integration/main7.c new file mode 100644 index 0000000..22427e3 --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main8.c b/unit-tests/test-cases/llvm-integration/main8.c new file mode 100644 index 0000000..a9c924d --- /dev/null +++ b/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/unit-tests/test-cases/llvm-integration/main9.c b/unit-tests/test-cases/llvm-integration/main9.c new file mode 100644 index 0000000..b44bf6e --- /dev/null +++ b/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/unit-tests/test-cases/loader_path/Makefile b/unit-tests/test-cases/loader_path/Makefile new file mode 100644 index 0000000..126c10d --- /dev/null +++ b/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/unit-tests/test-cases/loader_path/bar.c b/unit-tests/test-cases/loader_path/bar.c new file mode 100644 index 0000000..a307157 --- /dev/null +++ b/unit-tests/test-cases/loader_path/bar.c @@ -0,0 +1,6 @@ + +int bar() +{ + return 1; +} + diff --git a/unit-tests/test-cases/loader_path/foo.c b/unit-tests/test-cases/loader_path/foo.c new file mode 100644 index 0000000..8c2179d --- /dev/null +++ b/unit-tests/test-cases/loader_path/foo.c @@ -0,0 +1,7 @@ + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/unit-tests/test-cases/loader_path/main.c b/unit-tests/test-cases/loader_path/main.c new file mode 100644 index 0000000..829ca5e --- /dev/null +++ b/unit-tests/test-cases/loader_path/main.c @@ -0,0 +1,8 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} + diff --git a/unit-tests/test-cases/local-symbol-partial-stripping/Makefile b/unit-tests/test-cases/local-symbol-partial-stripping/Makefile new file mode 100644 index 0000000..8fc14f7 --- /dev/null +++ b/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/unit-tests/test-cases/local-symbol-partial-stripping/a.expect b/unit-tests/test-cases/local-symbol-partial-stripping/a.expect new file mode 100644 index 0000000..9ecbf53 --- /dev/null +++ b/unit-tests/test-cases/local-symbol-partial-stripping/a.expect @@ -0,0 +1,2 @@ +< _myglobal +< _xmyglobal2 diff --git a/unit-tests/test-cases/local-symbol-partial-stripping/a.list b/unit-tests/test-cases/local-symbol-partial-stripping/a.list new file mode 100644 index 0000000..16c59a2 --- /dev/null +++ b/unit-tests/test-cases/local-symbol-partial-stripping/a.list @@ -0,0 +1,2 @@ +_myglobal +_xmyglobal2 diff --git a/unit-tests/test-cases/local-symbol-partial-stripping/b.expect b/unit-tests/test-cases/local-symbol-partial-stripping/b.expect new file mode 100644 index 0000000..f1d15e4 --- /dev/null +++ b/unit-tests/test-cases/local-symbol-partial-stripping/b.expect @@ -0,0 +1,3 @@ +< _myfunction +< _myglobal2 +< _xmyglobal2 diff --git a/unit-tests/test-cases/local-symbol-partial-stripping/b.list b/unit-tests/test-cases/local-symbol-partial-stripping/b.list new file mode 100644 index 0000000..97170ac --- /dev/null +++ b/unit-tests/test-cases/local-symbol-partial-stripping/b.list @@ -0,0 +1,2 @@ +*2 +_myf*on diff --git a/unit-tests/test-cases/local-symbol-partial-stripping/c.list b/unit-tests/test-cases/local-symbol-partial-stripping/c.list new file mode 100644 index 0000000..be715cd --- /dev/null +++ b/unit-tests/test-cases/local-symbol-partial-stripping/c.list @@ -0,0 +1 @@ +*my* diff --git a/unit-tests/test-cases/local-symbol-partial-stripping/foo.c b/unit-tests/test-cases/local-symbol-partial-stripping/foo.c new file mode 100644 index 0000000..42e6cb7 --- /dev/null +++ b/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/unit-tests/test-cases/local-symbol-partial-stripping/main.c b/unit-tests/test-cases/local-symbol-partial-stripping/main.c new file mode 100644 index 0000000..013bc55 --- /dev/null +++ b/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/unit-tests/test-cases/main-stripped/Makefile b/unit-tests/test-cases/main-stripped/Makefile new file mode 100644 index 0000000..03756ff --- /dev/null +++ b/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/unit-tests/test-cases/main-stripped/main.c b/unit-tests/test-cases/main-stripped/main.c new file mode 100644 index 0000000..1e71f1b --- /dev/null +++ b/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/unit-tests/test-cases/main-stripped/main.exp b/unit-tests/test-cases/main-stripped/main.exp new file mode 100644 index 0000000..4eb9e89 --- /dev/null +++ b/unit-tests/test-cases/main-stripped/main.exp @@ -0,0 +1 @@ +_main diff --git a/unit-tests/test-cases/missing-option-args/Makefile b/unit-tests/test-cases/missing-option-args/Makefile new file mode 100644 index 0000000..81c3f58 --- /dev/null +++ b/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/unit-tests/test-cases/missing-option-args/comment.txt b/unit-tests/test-cases/missing-option-args/comment.txt new file mode 100644 index 0000000..8000102 --- /dev/null +++ b/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/unit-tests/test-cases/multiple-entry-points/Makefile b/unit-tests/test-cases/multiple-entry-points/Makefile index 0a8dee0..136bcf6 100644 --- a/unit-tests/test-cases/multiple-entry-points/Makefile +++ b/unit-tests/test-cases/multiple-entry-points/Makefile @@ -35,13 +35,12 @@ 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 - ${OBJECTDUMP} -no_sort test.${ARCH}.o > test.${ARCH}.o.dump - ${OBJECTDUMP} -no_sort test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${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 test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump - - - + rm -rf *.o *.dump diff --git a/unit-tests/test-cases/multiple-entry-points/comment.txt b/unit-tests/test-cases/multiple-entry-points/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/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/unit-tests/test-cases/no-dynamic-common/Makefile.newtest b/unit-tests/test-cases/no-dynamic-common/Makefile.newtest new file mode 100644 index 0000000..d0f7df8 --- /dev/null +++ b/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/unit-tests/test-cases/no-dynamic-common/a.c b/unit-tests/test-cases/no-dynamic-common/a.c new file mode 100644 index 0000000..8f92c9d --- /dev/null +++ b/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/unit-tests/test-cases/no-dynamic-common/comment.txt b/unit-tests/test-cases/no-dynamic-common/comment.txt new file mode 100644 index 0000000..a5a960d --- /dev/null +++ b/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/unit-tests/test-cases/no-dynamic-common/test.c b/unit-tests/test-cases/no-dynamic-common/test.c new file mode 100644 index 0000000..f34267a --- /dev/null +++ b/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/unit-tests/test-cases/no-uuid/Makefile b/unit-tests/test-cases/no-uuid/Makefile index 223c8b8..c24abec 100644 --- a/unit-tests/test-cases/no-uuid/Makefile +++ b/unit-tests/test-cases/no-uuid/Makefile @@ -38,17 +38,26 @@ all: rm -f foo-${ARCH} # Test with stabs - ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -gfull + ${CC} ${CCFLAGS} foo.c -o foo-${ARCH} -gfull -gstabs+ ${FAIL_IF_BAD_MACHO} foo-${ARCH} ${OTOOL} -hlv foo-${ARCH} | grep LC_UUID | ${FAIL_IF_STDIN} # Test with two files one with UUID ${CC} ${CCFLAGS} bar.c -c -gdwarf-2 + ${FAIL_IF_BAD_OBJ} bar.o + ${LD} -arch ${ARCH} bar.o -r -o bar-${ARCH}.o -no_uuid + ${FAIL_IF_BAD_OBJ} bar-${ARCH}.o + ${CC} ${CCFLAGS} foo.c -c -gdwarf-2 + ${FAIL_IF_BAD_OBJ} foo.o + ${LD} -arch ${ARCH} foo.o -r -o foo-${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o + ${CC} ${CCFLAGS} foo-${ARCH}.o bar-${ARCH}.o -o temp-${ARCH} ${FAIL_IF_BAD_MACHO} temp-${ARCH} + ${OTOOL} -hlv temp-${ARCH} | grep LC_UUID | ${FAIL_IF_EMPTY} rm -f foo.o bar.o foo-${ARCH}.o bar-${ARCH}.o temp-${ARCH} @@ -58,4 +67,4 @@ all: ${OTOOL} -hlv foo-${ARCH} | grep LC_UUID | ${PASS_IFF_EMPTY} clean: - rm -rf foo-${ARCH} bar-${ARCH}.dylib temp-${ARCH}.dylib + rm -rf foo-* diff --git a/unit-tests/test-cases/no-uuid/comment.txt b/unit-tests/test-cases/no-uuid/comment.txt new file mode 100644 index 0000000..269bbbd --- /dev/null +++ b/unit-tests/test-cases/no-uuid/comment.txt @@ -0,0 +1 @@ +Test the we set emit LC_UUID correctly diff --git a/unit-tests/test-cases/non-lazy-r/Makefile b/unit-tests/test-cases/non-lazy-r/Makefile new file mode 100644 index 0000000..28f94d6 --- /dev/null +++ b/unit-tests/test-cases/non-lazy-r/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 non-lazy-pointers are properly handled by -r +# + + +all: all-${ARCH} + +all-ppc: hasnl + +all-ppc64: hasnl + +all-i386: 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/unit-tests/test-cases/non-lazy-r/foo.c b/unit-tests/test-cases/non-lazy-r/foo.c new file mode 100644 index 0000000..1fa325e --- /dev/null +++ b/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/unit-tests/test-cases/non-lazy-r/other.c b/unit-tests/test-cases/non-lazy-r/other.c new file mode 100644 index 0000000..6420437 --- /dev/null +++ b/unit-tests/test-cases/non-lazy-r/other.c @@ -0,0 +1,2 @@ +int foo = 2; +int other = 3; diff --git a/unit-tests/test-cases/objc-gc-checks/Makefile b/unit-tests/test-cases/objc-gc-checks/Makefile new file mode 100644 index 0000000..ed12e39 --- /dev/null +++ b/unit-tests/test-cases/objc-gc-checks/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 + +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} foo.o bar.o -dynamiclib -o libfoobar.dylib -framework Foundation + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + + # check GC/RR + GC/RR -> GC/RR + ${CC} foo-gc.o bar-gc.o -dynamiclib -o libfoobar.dylib -framework Foundation + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + + # check GC + GC -> GC + ${CC} foo-gc-only.o bar-gc-only.o -dynamiclib -o libfoobar.dylib -framework Foundation + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + + # check RR + GC/RR -> error + ${FAIL_IF_SUCCESS} ${CC} foo.o bar-gc.o -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log + + # check RR + GC -> error + ${FAIL_IF_SUCCESS} ${CC} foo.o bar-gc-only.o -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log + + # check GC + GC/RR -> error + ${FAIL_IF_SUCCESS} ${CC} foo-gc-only.o bar-gc.o -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log + ${PASS_IFF} true + +clean: + rm -rf foo*.o bar*.o libfoobar.dylib fail.log diff --git a/unit-tests/test-cases/objc-gc-checks/bar.m b/unit-tests/test-cases/objc-gc-checks/bar.m new file mode 100644 index 0000000..f66df50 --- /dev/null +++ b/unit-tests/test-cases/objc-gc-checks/bar.m @@ -0,0 +1,12 @@ +#include + +@interface Bar : NSObject { + int f; +} +- (void) doit; +@end + +@implementation Bar +- (void) doit { } +@end + diff --git a/unit-tests/test-cases/objc-gc-checks/comment.txt b/unit-tests/test-cases/objc-gc-checks/comment.txt new file mode 100644 index 0000000..953da58 --- /dev/null +++ b/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/unit-tests/test-cases/objc-gc-checks/foo.m b/unit-tests/test-cases/objc-gc-checks/foo.m new file mode 100644 index 0000000..5324bea --- /dev/null +++ b/unit-tests/test-cases/objc-gc-checks/foo.m @@ -0,0 +1,12 @@ +#include + +@interface Foo : NSObject { + int f; +} +- (void) doit; +@end + +@implementation Foo +- (void) doit { } +@end + diff --git a/unit-tests/test-cases/objc-references/Makefile b/unit-tests/test-cases/objc-references/Makefile index 169963b..a11f1d0 100644 --- a/unit-tests/test-cases/objc-references/Makefile +++ b/unit-tests/test-cases/objc-references/Makefile @@ -33,16 +33,15 @@ 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 - nm test-r.${ARCH}.o | grep '.objc_class_name_NSObject' | ${FAIL_IF_EMPTY} - nm test-r.${ARCH}.o | grep '.objc_class_name_NSData' | ${FAIL_IF_EMPTY} - nm test-r.${ARCH}.o | grep '.objc_class_name_NSArray' | ${FAIL_IF_EMPTY} - nm test-r.${ARCH}.o | grep '.objc_class_name_NSString' | ${PASS_IFF_STDIN} + ${FAIL_IF_BAD_OBJ} test-r.${ARCH}.o -clean: - rm -rf test.${ARCH}.o test.${ARCH}.o.dump - + 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} - #${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - #grep '.objc_class_name_NSObject' test.${ARCH}.o.dump | ${FAIL_IF_EMPTY} - #grep '.objc_class_name_NSString' test.${ARCH}.o.dump | ${PASS_IFF_STDIN} +clean: + rm -rf *.o *.dump diff --git a/unit-tests/test-cases/objc-references/comment.txt b/unit-tests/test-cases/objc-references/comment.txt new file mode 100644 index 0000000..ce67b71 --- /dev/null +++ b/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/unit-tests/test-cases/order_file-ans/Makefile b/unit-tests/test-cases/order_file-ans/Makefile new file mode 100644 index 0000000..23fe568 --- /dev/null +++ b/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/unit-tests/test-cases/order_file-ans/main.cxx b/unit-tests/test-cases/order_file-ans/main.cxx new file mode 100644 index 0000000..b0412f9 --- /dev/null +++ b/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/unit-tests/test-cases/order_file-ans/main.expected b/unit-tests/test-cases/order_file-ans/main.expected new file mode 100644 index 0000000..75e104f --- /dev/null +++ b/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/unit-tests/test-cases/order_file-ans/main.order b/unit-tests/test-cases/order_file-ans/main.order new file mode 100644 index 0000000..36dd786 --- /dev/null +++ b/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/unit-tests/test-cases/order_file/Makefile b/unit-tests/test-cases/order_file/Makefile new file mode 100644 index 0000000..21ae0ea --- /dev/null +++ b/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/unit-tests/test-cases/order_file/extra.s b/unit-tests/test-cases/order_file/extra.s new file mode 100644 index 0000000..90166ce --- /dev/null +++ b/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/unit-tests/test-cases/order_file/main.c b/unit-tests/test-cases/order_file/main.c new file mode 100644 index 0000000..5643b45 --- /dev/null +++ b/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/unit-tests/test-cases/order_file/main1.expected b/unit-tests/test-cases/order_file/main1.expected new file mode 100644 index 0000000..04d128b --- /dev/null +++ b/unit-tests/test-cases/order_file/main1.expected @@ -0,0 +1,4 @@ +_main4 +_main3 +_main +_main2 diff --git a/unit-tests/test-cases/order_file/main1.order b/unit-tests/test-cases/order_file/main1.order new file mode 100644 index 0000000..06b34d5 --- /dev/null +++ b/unit-tests/test-cases/order_file/main1.order @@ -0,0 +1,4 @@ +_main4 +_main3 + + diff --git a/unit-tests/test-cases/order_file/main2.expected b/unit-tests/test-cases/order_file/main2.expected new file mode 100644 index 0000000..8aca65c --- /dev/null +++ b/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/unit-tests/test-cases/order_file/main2.order b/unit-tests/test-cases/order_file/main2.order new file mode 100644 index 0000000..87f89e6 --- /dev/null +++ b/unit-tests/test-cases/order_file/main2.order @@ -0,0 +1,6 @@ +_main3 +_aaa3 +_main4 + + + diff --git a/unit-tests/test-cases/order_file/main3.expected b/unit-tests/test-cases/order_file/main3.expected new file mode 100644 index 0000000..04d128b --- /dev/null +++ b/unit-tests/test-cases/order_file/main3.expected @@ -0,0 +1,4 @@ +_main4 +_main3 +_main +_main2 diff --git a/unit-tests/test-cases/order_file/main3.order b/unit-tests/test-cases/order_file/main3.order new file mode 100644 index 0000000..d135527 --- /dev/null +++ b/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/unit-tests/test-cases/prebound-split-seg/Makefile b/unit-tests/test-cases/prebound-split-seg/Makefile new file mode 100644 index 0000000..07bce59 --- /dev/null +++ b/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/unit-tests/test-cases/prebound-split-seg/address_table b/unit-tests/test-cases/prebound-split-seg/address_table new file mode 100644 index 0000000..b611ca8 --- /dev/null +++ b/unit-tests/test-cases/prebound-split-seg/address_table @@ -0,0 +1,4 @@ +# comment +0x91000000 0xA1000000 /foo/bar/libbar.dylib +# + diff --git a/unit-tests/test-cases/prebound-split-seg/bar.c b/unit-tests/test-cases/prebound-split-seg/bar.c new file mode 100644 index 0000000..46b7269 --- /dev/null +++ b/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/unit-tests/test-cases/private-non-lazy/Makefile b/unit-tests/test-cases/private-non-lazy/Makefile index 655631c..be0ba42 100644 --- a/unit-tests/test-cases/private-non-lazy/Makefile +++ b/unit-tests/test-cases/private-non-lazy/Makefile @@ -33,15 +33,22 @@ 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 foo.o bar.o foobar.o hello foobar2.o hello2 - - + rm -rf *.o hello hello2 diff --git a/unit-tests/test-cases/private-non-lazy/comment.txt b/unit-tests/test-cases/private-non-lazy/comment.txt new file mode 100644 index 0000000..e6d11c0 --- /dev/null +++ b/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/unit-tests/test-cases/re-export-cases/Makefile b/unit-tests/test-cases/re-export-cases/Makefile new file mode 100644 index 0000000..64ec112 --- /dev/null +++ b/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/unit-tests/test-cases/re-export-cases/bar.c b/unit-tests/test-cases/re-export-cases/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/unit-tests/test-cases/re-export-cases/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-cases/baz.c b/unit-tests/test-cases/re-export-cases/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/unit-tests/test-cases/re-export-cases/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-cases/foo.c b/unit-tests/test-cases/re-export-cases/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/re-export-cases/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-flag/Makefile b/unit-tests/test-cases/re-export-flag/Makefile new file mode 100644 index 0000000..82228ee --- /dev/null +++ b/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/unit-tests/test-cases/re-export-flag/bar.c b/unit-tests/test-cases/re-export-flag/bar.c new file mode 100644 index 0000000..34e5666 --- /dev/null +++ b/unit-tests/test-cases/re-export-flag/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-flag/foo.c b/unit-tests/test-cases/re-export-flag/foo.c new file mode 100644 index 0000000..714540a --- /dev/null +++ b/unit-tests/test-cases/re-export-flag/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-optimizations/Makefile b/unit-tests/test-cases/re-export-optimizations/Makefile new file mode 100644 index 0000000..a1dfd88 --- /dev/null +++ b/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/unit-tests/test-cases/re-export-optimizations/bar.c b/unit-tests/test-cases/re-export-optimizations/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/unit-tests/test-cases/re-export-optimizations/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-optimizations/foo.c b/unit-tests/test-cases/re-export-optimizations/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/re-export-optimizations/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-optimizations/main.c b/unit-tests/test-cases/re-export-optimizations/main.c new file mode 100644 index 0000000..2b85b0e --- /dev/null +++ b/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/unit-tests/test-cases/re-export-relative-paths/Makefile b/unit-tests/test-cases/re-export-relative-paths/Makefile new file mode 100644 index 0000000..2560a86 --- /dev/null +++ b/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/unit-tests/test-cases/re-export-relative-paths/bar.c b/unit-tests/test-cases/re-export-relative-paths/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/unit-tests/test-cases/re-export-relative-paths/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-relative-paths/foo.c b/unit-tests/test-cases/re-export-relative-paths/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/re-export-relative-paths/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-relative-paths/main.c b/unit-tests/test-cases/re-export-relative-paths/main.c new file mode 100644 index 0000000..367c6cb --- /dev/null +++ b/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/unit-tests/test-cases/re-export-relative-paths/wrap.c b/unit-tests/test-cases/re-export-relative-paths/wrap.c new file mode 100644 index 0000000..d3cdd85 --- /dev/null +++ b/unit-tests/test-cases/re-export-relative-paths/wrap.c @@ -0,0 +1,2 @@ +int wrap() { return 0; } + diff --git a/unit-tests/test-cases/read-only-relocs/Makefile b/unit-tests/test-cases/read-only-relocs/Makefile index 6cea9e2..53d702a 100644 --- a/unit-tests/test-cases/read-only-relocs/Makefile +++ b/unit-tests/test-cases/read-only-relocs/Makefile @@ -2,14 +2,14 @@ # Copyright (c) 2006 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ -# +# # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. -# +# # The Original Code and all software distributed under the 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,7 +17,7 @@ # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. -# +# # @APPLE_LICENSE_HEADER_END@ ## TESTROOT = ../.. @@ -31,12 +31,14 @@ include ${TESTROOT}/include/common.makefile SHELL = bash # use bash shell so we can redirect just stderr -run: all - +run: + @if [ ${ARCH} = x86_64 ] ; then \ + ${PASS_IFF} /usr/bin/true ; \ + else \ + $(MAKE) all ; \ + fi all: ${FAIL_IFF} ${CC} ${CCFLAGS} test.c -mdynamic-no-pic -dynamiclib -o test-${ARCH}.dylib 2> fail.log clean: - rm test-${ARCH}.dylib fail.log - - + rm -rf *.dylib fail.log diff --git a/unit-tests/test-cases/read-only-relocs/comment.txt b/unit-tests/test-cases/read-only-relocs/comment.txt new file mode 100644 index 0000000..f319d31 --- /dev/null +++ b/unit-tests/test-cases/read-only-relocs/comment.txt @@ -0,0 +1 @@ +The point of this test is a sanity check that ld will fail to link a dylib compiled with -mdynamic-no-pic diff --git a/unit-tests/test-cases/rebase-basic/Makefile b/unit-tests/test-cases/rebase-basic/Makefile index 2077033..77bdf6b 100644 --- a/unit-tests/test-cases/rebase-basic/Makefile +++ b/unit-tests/test-cases/rebase-basic/Makefile @@ -32,15 +32,22 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} -arch ${ARCH} -c foo.c -o foo.${ARCH}.o - ${CC} -arch ${ARCH} -c bar.m -o bar.${ARCH}.o - ${CC} -arch ${ARCH} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -o libfoo.${ARCH}.dylib -framework Foundation -single_module -mmacosx-version-min=10.5 - ${CC} -arch ${ARCH} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -o libfoo-alt.${ARCH}.dylib -framework Foundation -single_module -mmacosx-version-min=10.5 -seg1addr 0x12340000 -install_name libfoo.${ARCH}.dylib + ${CC} -c foo.c -o foo.${ARCH}.o + ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o + + ${CC} -c bar.m -o bar.${ARCH}.o + ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o + + ${CC} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -o libfoo.${ARCH}.dylib -framework Foundation -single_module -mmacosx-version-min=10.5 + ${FAIL_IF_BAD_MACHO} libfoo.${ARCH}.dylib + + ${CC} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -o libfoo-alt.${ARCH}.dylib -framework Foundation -single_module -mmacosx-version-min=10.5 -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 foo.${ARCH}.o bar.${ARCH}.o libfoo.${ARCH}.dylib libfoo-alt.${ARCH}.dylib - - + rm *.o *.dylib diff --git a/unit-tests/test-cases/rebase-basic/comment.txt b/unit-tests/test-cases/rebase-basic/comment.txt new file mode 100644 index 0000000..013eb45 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-asm/Makefile b/unit-tests/test-cases/relocs-asm/Makefile index 6c0eb7b..c231ac4 100644 --- a/unit-tests/test-cases/relocs-asm/Makefile +++ b/unit-tests/test-cases/relocs-asm/Makefile @@ -35,13 +35,12 @@ 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 - ${OBJECTDUMP} -no_content relocs-asm.${ARCH}.o > relocs-asm.${ARCH}.o.dump - ${OBJECTDUMP} -no_content relocs-asm-r.${ARCH}.o > relocs-asm-r.${ARCH}.o.dump + ${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 relocs-asm.${ARCH}.o relocs-asm-r.${ARCH}.o relocs-asm.${ARCH}.o.dump relocs-asm-r.${ARCH}.o.dump - - - + rm -rf *.o *.dump diff --git a/unit-tests/test-cases/relocs-asm/comment.txt b/unit-tests/test-cases/relocs-asm/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-asm/relocs-asm.s b/unit-tests/test-cases/relocs-asm/relocs-asm.s index afacc62..28cfc56 100644 --- a/unit-tests/test-cases/relocs-asm/relocs-asm.s +++ b/unit-tests/test-cases/relocs-asm/relocs-asm.s @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -186,6 +186,18 @@ _test_branches: # 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 + #endif @@ -272,10 +284,35 @@ _test_diffs: 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 diff --git a/unit-tests/test-cases/relocs-c/Makefile b/unit-tests/test-cases/relocs-c/Makefile index 610a02b..15a7c35 100644 --- a/unit-tests/test-cases/relocs-c/Makefile +++ b/unit-tests/test-cases/relocs-c/Makefile @@ -36,14 +36,14 @@ run: all all: ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o - ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o - ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump + ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump #grep "plus" test.${ARCH}.o.dump | ${FAIL_IF_STDIN} - ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${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 #grep "plus" test-r.${ARCH}.o.dump | ${FAIL_IF_STDIN} + ${PASS_IFF} diff test.${ARCH}.o.dump test-r.${ARCH}.o.dump clean: - rm -rf test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump - - + rm -rf *.o *.dump diff --git a/unit-tests/test-cases/relocs-c2/Makefile b/unit-tests/test-cases/relocs-c2/Makefile new file mode 100644 index 0000000..1a2991a --- /dev/null +++ b/unit-tests/test-cases/relocs-c2/Makefile @@ -0,0 +1,59 @@ +## +# 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 +# + +LD=ld64 + +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/unit-tests/test-cases/relocs-c2/comment.txt b/unit-tests/test-cases/relocs-c2/comment.txt new file mode 100644 index 0000000..2499674 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-c2/test.c b/unit-tests/test-cases/relocs-c2/test.c new file mode 100644 index 0000000..b877760 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-literals/Makefile b/unit-tests/test-cases/relocs-literals/Makefile index 6d45efd..a9ca5ef 100644 --- a/unit-tests/test-cases/relocs-literals/Makefile +++ b/unit-tests/test-cases/relocs-literals/Makefile @@ -2,14 +2,14 @@ # 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,7 +17,7 @@ # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. -# +# # @APPLE_LICENSE_HEADER_END@ ## TESTROOT = ../.. @@ -35,13 +35,13 @@ 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 + ${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 - ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${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 test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump - - + rm -rf *.o *.dump diff --git a/unit-tests/test-cases/relocs-literals/test.c b/unit-tests/test-cases/relocs-literals/test.c index 31e87c2..2d199d0 100644 --- a/unit-tests/test-cases/relocs-literals/test.c +++ b/unit-tests/test-cases/relocs-literals/test.c @@ -45,3 +45,10 @@ 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/unit-tests/test-cases/relocs-literals2/Makefile b/unit-tests/test-cases/relocs-literals2/Makefile new file mode 100644 index 0000000..23e4a82 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-literals2/test.c b/unit-tests/test-cases/relocs-literals2/test.c new file mode 100644 index 0000000..2d199d0 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-literals3/Makefile b/unit-tests/test-cases/relocs-literals3/Makefile new file mode 100644 index 0000000..66190ae --- /dev/null +++ b/unit-tests/test-cases/relocs-literals3/Makefile @@ -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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile +LD=ld64 + + +# +# 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/unit-tests/test-cases/relocs-literals3/comment.txt b/unit-tests/test-cases/relocs-literals3/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-literals3/test.c b/unit-tests/test-cases/relocs-literals3/test.c new file mode 100644 index 0000000..31e87c2 --- /dev/null +++ b/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/unit-tests/test-cases/relocs-objc/Makefile b/unit-tests/test-cases/relocs-objc/Makefile index 9b55f25..0f8846d 100644 --- a/unit-tests/test-cases/relocs-objc/Makefile +++ b/unit-tests/test-cases/relocs-objc/Makefile @@ -36,12 +36,12 @@ 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 - ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump - ${OBJECTDUMP} -no_content test-r.${ARCH}.o > test-r.${ARCH}.o.dump + ${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 test.${ARCH}.o test-r.${ARCH}.o test.${ARCH}.o.dump test-r.${ARCH}.o.dump - - + rm -rf *.o *.dump diff --git a/unit-tests/test-cases/relocs-objc/comment.txt b/unit-tests/test-cases/relocs-objc/comment.txt new file mode 100644 index 0000000..4e819e1 --- /dev/null +++ b/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/unit-tests/test-cases/segment-order/Makefile b/unit-tests/test-cases/segment-order/Makefile new file mode 100644 index 0000000..32c8e81 --- /dev/null +++ b/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/unit-tests/test-cases/segment-order/expected.order b/unit-tests/test-cases/segment-order/expected.order new file mode 100644 index 0000000..e05b042 --- /dev/null +++ b/unit-tests/test-cases/segment-order/expected.order @@ -0,0 +1,3 @@ +_sym_kkk +_sym_jjj +_sym_lll diff --git a/unit-tests/test-cases/segment-order/main.c b/unit-tests/test-cases/segment-order/main.c new file mode 100644 index 0000000..df77448 --- /dev/null +++ b/unit-tests/test-cases/segment-order/main.c @@ -0,0 +1,4 @@ + + +int main() { return 0; } + diff --git a/unit-tests/test-cases/segment-order/segJJJ.s b/unit-tests/test-cases/segment-order/segJJJ.s new file mode 100644 index 0000000..d9f5f71 --- /dev/null +++ b/unit-tests/test-cases/segment-order/segJJJ.s @@ -0,0 +1,7 @@ + + .section __JJJ,__jjj +_sym_jjj: .space 128 + + + + diff --git a/unit-tests/test-cases/segment-order/segKKK.s b/unit-tests/test-cases/segment-order/segKKK.s new file mode 100644 index 0000000..70b1952 --- /dev/null +++ b/unit-tests/test-cases/segment-order/segKKK.s @@ -0,0 +1,7 @@ + + .section __KKK,__kkk +_sym_kkk: .space 128 + + + + diff --git a/unit-tests/test-cases/segment-order/segLLL.s b/unit-tests/test-cases/segment-order/segLLL.s new file mode 100644 index 0000000..045eea4 --- /dev/null +++ b/unit-tests/test-cases/segment-order/segLLL.s @@ -0,0 +1,7 @@ + + .section __LLL,__lll +_sym_lll: .space 128 + + + + diff --git a/unit-tests/test-cases/special-labels/Makefile b/unit-tests/test-cases/special-labels/Makefile new file mode 100644 index 0000000..060f12e --- /dev/null +++ b/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/unit-tests/test-cases/special-labels/extra.s b/unit-tests/test-cases/special-labels/extra.s new file mode 100644 index 0000000..0755508 --- /dev/null +++ b/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/unit-tests/test-cases/special-labels/main.c b/unit-tests/test-cases/special-labels/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/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/unit-tests/test-cases/stabs-coalesce/Makefile b/unit-tests/test-cases/stabs-coalesce/Makefile index c3414e0..6e11c59 100644 --- a/unit-tests/test-cases/stabs-coalesce/Makefile +++ b/unit-tests/test-cases/stabs-coalesce/Makefile @@ -34,19 +34,19 @@ include ${TESTROOT}/include/common.makefile run: all all: hello.o other.o - ${CXX} ${CCXXFLAGS} -gused hello.o other.o -o stabs-hello-${ARCH} + ${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} -gused hello.cxx -c -o hello.o + ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ + ${FAIL_IF_BAD_OBJ} $@ other.o : other.cxx - ${CXX} ${CCXXFLAGS} -gused other.cxx -c -o other.o + ${CXX} ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ + ${FAIL_IF_BAD_OBJ} $@ clean: - rm -rf stabs-hello-${ARCH} hello.o other.o stabs-hello-${ARCH}.stabs stabs-hello-foo-count one - - + rm -rf stabs-hello-* *.o *.stabs stabs-hello-foo-count one diff --git a/unit-tests/test-cases/stabs-coalesce/comment.txt b/unit-tests/test-cases/stabs-coalesce/comment.txt new file mode 100644 index 0000000..f22b9a1 --- /dev/null +++ b/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/unit-tests/test-cases/stabs-directory-slash/Makefile b/unit-tests/test-cases/stabs-directory-slash/Makefile new file mode 100644 index 0000000..5318933 --- /dev/null +++ b/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/unit-tests/test-cases/stabs-directory-slash/main.c b/unit-tests/test-cases/stabs-directory-slash/main.c new file mode 100644 index 0000000..54dc4c5 --- /dev/null +++ b/unit-tests/test-cases/stabs-directory-slash/main.c @@ -0,0 +1,3 @@ +main() +{ +} diff --git a/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest b/unit-tests/test-cases/stack_addr_no_size/Makefile.newtest new file mode 100644 index 0000000..a9e452f --- /dev/null +++ b/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/unit-tests/test-cases/stack_addr_no_size/comment.txt b/unit-tests/test-cases/stack_addr_no_size/comment.txt new file mode 100644 index 0000000..da74f89 --- /dev/null +++ b/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/unit-tests/test-cases/stack_addr_no_size/main.c b/unit-tests/test-cases/stack_addr_no_size/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/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/unit-tests/test-cases/stack_addr_size/Makefile b/unit-tests/test-cases/stack_addr_size/Makefile new file mode 100644 index 0000000..49e6467 --- /dev/null +++ b/unit-tests/test-cases/stack_addr_size/Makefile @@ -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 the ld commands -stack_addr, -stack_size +# Test using both -stack_add and -stack_size + +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xCC000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0xc7000000 +else + STACK_ADDR = 0x110000000 + STACK_TOP = 0x000000010b000000 + STACK_SIZE = 0x0000000005000000 +endif + + +run: all + + + +all: +# info seems to not work, use warning: + ${CC} ${CCFLAGS} -c main.c -o main-${ARCH}.o + ${FAIL_IF_BAD_OBJ} main-${ARCH}.o + + ${LD} -arch ${ARCH} \ + -stack_addr ${STACK_ADDR} -stack_size ${STACK_SIZE} \ + -lcrt1.o -lSystem \ + main-${ARCH}.o -o main \ +# # Can check warning if desired. +# > lderr.out +#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 " vmaddr[ ]*${STACK_TOP}" ldcmds.out | ${FAIL_IF_EMPTY} + grep " vmsize[ ]*${STACK_SIZE}" ldcmds.out | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf *.o *.err *.out main diff --git a/unit-tests/test-cases/stack_addr_size/comment.txt b/unit-tests/test-cases/stack_addr_size/comment.txt new file mode 100644 index 0000000..da74f89 --- /dev/null +++ b/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/unit-tests/test-cases/stack_addr_size/main.c b/unit-tests/test-cases/stack_addr_size/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/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/unit-tests/test-cases/stack_size_no_addr/Makefile b/unit-tests/test-cases/stack_size_no_addr/Makefile new file mode 100644 index 0000000..a13d767 --- /dev/null +++ b/unit-tests/test-cases/stack_size_no_addr/Makefile @@ -0,0 +1,78 @@ +## +# 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_size only + +ifeq (,${findstring 64,$(ARCH)}) + STACK_ADDR = 0xC0000000 + STACK_SIZE = 0x05000000 + STACK_TOP = 0xbb000000 +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: + ${CC} ${CCFLAGS} -c main.c -o main-${ARCH}.o + ${FAIL_IF_BAD_OBJ} main-${ARCH}.o + + + ${LD} -arch ${ARCH} \ + -stack_size ${STACK_SIZE} \ + -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/unit-tests/test-cases/stack_size_no_addr/comment.txt b/unit-tests/test-cases/stack_size_no_addr/comment.txt new file mode 100644 index 0000000..5933975 --- /dev/null +++ b/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/unit-tests/test-cases/stack_size_no_addr/main.c b/unit-tests/test-cases/stack_size_no_addr/main.c new file mode 100644 index 0000000..5c73586 --- /dev/null +++ b/unit-tests/test-cases/stack_size_no_addr/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/unit-tests/test-cases/static-executable/Makefile b/unit-tests/test-cases/static-executable/Makefile index cee5f44..8e14133 100644 --- a/unit-tests/test-cases/static-executable/Makefile +++ b/unit-tests/test-cases/static-executable/Makefile @@ -2,14 +2,14 @@ # Copyright (c) 2006 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ -# +# # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. -# +# # The Original Code and all software distributed under the 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,7 +17,7 @@ # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. -# +# # @APPLE_LICENSE_HEADER_END@ ## TESTROOT = ../.. @@ -28,13 +28,16 @@ include ${TESTROOT}/include/common.makefile # can link a static executable (requires non-public archives) # -run: all +run: + @if [ ${ARCH} = ppc ] || [ ${ARCH} = i386 ] ; then \ + $(MAKE) all ; \ + else \ + $(PASS_IFF) /usr/bin/true ; \ + fi all: - ${CC} ${CCFLAGS} test.c -static -o test-${ARCH} -L/usr/local/lib/system -lc_static -lm_static + ${CC} ${CCFLAGS} test.c -static -o test-${ARCH} -L/usr/local/lib/system -lc_static -lm_static -dead_strip 2>/dev/null ${PASS_IFF_GOOD_MACHO} test-${ARCH} clean: - rm test-${ARCH} - - + rm -rf test-* diff --git a/unit-tests/test-cases/static-executable/comment.txt b/unit-tests/test-cases/static-executable/comment.txt new file mode 100644 index 0000000..bc535a3 --- /dev/null +++ b/unit-tests/test-cases/static-executable/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/unit-tests/test-cases/static-strip/Makefile.newtest b/unit-tests/test-cases/static-strip/Makefile.newtest new file mode 100644 index 0000000..343855e --- /dev/null +++ b/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 + ${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/unit-tests/test-cases/static-strip/comment.txt b/unit-tests/test-cases/static-strip/comment.txt new file mode 100644 index 0000000..bc535a3 --- /dev/null +++ b/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/unit-tests/test-cases/static-strip/test.c b/unit-tests/test-cases/static-strip/test.c new file mode 100644 index 0000000..ab472fb --- /dev/null +++ b/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/unit-tests/test-cases/strip-test2/Makefile b/unit-tests/test-cases/strip-test2/Makefile new file mode 100644 index 0000000..778770e --- /dev/null +++ b/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/unit-tests/test-cases/strip-test2/comment.txt b/unit-tests/test-cases/strip-test2/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/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/unit-tests/test-cases/strip-test2/main.cxx b/unit-tests/test-cases/strip-test2/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/unit-tests/test-cases/strip-test2/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/unit-tests/test-cases/strip-test3/Makefile.newtest b/unit-tests/test-cases/strip-test3/Makefile.newtest new file mode 100644 index 0000000..c1ad4c4 --- /dev/null +++ b/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/unit-tests/test-cases/strip-test3/comment.txt b/unit-tests/test-cases/strip-test3/comment.txt new file mode 100644 index 0000000..a99f78e --- /dev/null +++ b/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/unit-tests/test-cases/strip-test3/main.cxx b/unit-tests/test-cases/strip-test3/main.cxx new file mode 100644 index 0000000..dd65fef --- /dev/null +++ b/unit-tests/test-cases/strip-test3/main.cxx @@ -0,0 +1,6 @@ +#include +int main() +{ + std::vector stuff; + return 0; +} diff --git a/unit-tests/test-cases/strip_local/Makefile b/unit-tests/test-cases/strip_local/Makefile new file mode 100644 index 0000000..f32267c --- /dev/null +++ b/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/unit-tests/test-cases/strip_local/foo.c b/unit-tests/test-cases/strip_local/foo.c new file mode 100644 index 0000000..defa5eb --- /dev/null +++ b/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/unit-tests/test-cases/strip_local/hello.c b/unit-tests/test-cases/strip_local/hello.c new file mode 100644 index 0000000..2deed42 --- /dev/null +++ b/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/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile b/unit-tests/test-cases/stripped-indirect-symbol-table/Makefile new file mode 100644 index 0000000..c0647b3 --- /dev/null +++ b/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/unit-tests/test-cases/stripped-indirect-symbol-table/a.c b/unit-tests/test-cases/stripped-indirect-symbol-table/a.c new file mode 100644 index 0000000..141b6d9 --- /dev/null +++ b/unit-tests/test-cases/stripped-indirect-symbol-table/a.c @@ -0,0 +1,7 @@ + +int aData = 0; + +void a() +{ + ++aData; +} diff --git a/unit-tests/test-cases/stripped-indirect-symbol-table/b.c b/unit-tests/test-cases/stripped-indirect-symbol-table/b.c new file mode 100644 index 0000000..9608402 --- /dev/null +++ b/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/unit-tests/test-cases/stripped-indirect-symbol-table/c.c b/unit-tests/test-cases/stripped-indirect-symbol-table/c.c new file mode 100644 index 0000000..db8276a --- /dev/null +++ b/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/unit-tests/test-cases/stripped-indirect-symbol-table/func.c b/unit-tests/test-cases/stripped-indirect-symbol-table/func.c new file mode 100644 index 0000000..5724811 --- /dev/null +++ b/unit-tests/test-cases/stripped-indirect-symbol-table/func.c @@ -0,0 +1 @@ +void func(void* x) {} diff --git a/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list b/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list new file mode 100644 index 0000000..77ac6e9 --- /dev/null +++ b/unit-tests/test-cases/stripped-indirect-symbol-table/strip.list @@ -0,0 +1,2 @@ +_b +_bb diff --git a/unit-tests/test-cases/stub-generation/Makefile b/unit-tests/test-cases/stub-generation/Makefile new file mode 100644 index 0000000..9e6ecc0 --- /dev/null +++ b/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/unit-tests/test-cases/stub-generation/test.c b/unit-tests/test-cases/stub-generation/test.c new file mode 100644 index 0000000..4573622 --- /dev/null +++ b/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/unit-tests/test-cases/symbol-moving/Makefile b/unit-tests/test-cases/symbol-moving/Makefile new file mode 100644 index 0000000..fc85d7b --- /dev/null +++ b/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/unit-tests/test-cases/symbol-moving/aaa.c b/unit-tests/test-cases/symbol-moving/aaa.c new file mode 100644 index 0000000..71123eb --- /dev/null +++ b/unit-tests/test-cases/symbol-moving/aaa.c @@ -0,0 +1,3 @@ + +void aaa() {} + diff --git a/unit-tests/test-cases/symbol-moving/anotb.c b/unit-tests/test-cases/symbol-moving/anotb.c new file mode 100644 index 0000000..60fcf64 --- /dev/null +++ b/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/unit-tests/test-cases/symbol-moving/bar.c b/unit-tests/test-cases/symbol-moving/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/unit-tests/test-cases/symbol-moving/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/unit-tests/test-cases/symbol-moving/bbb.c b/unit-tests/test-cases/symbol-moving/bbb.c new file mode 100644 index 0000000..b6e9cc0 --- /dev/null +++ b/unit-tests/test-cases/symbol-moving/bbb.c @@ -0,0 +1 @@ +void bbb() {} diff --git a/unit-tests/test-cases/symbol-moving/bnota.c b/unit-tests/test-cases/symbol-moving/bnota.c new file mode 100644 index 0000000..d29b878 --- /dev/null +++ b/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/unit-tests/test-cases/symbol-moving/foo.c b/unit-tests/test-cases/symbol-moving/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/symbol-moving/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/symbol-moving/main.c b/unit-tests/test-cases/symbol-moving/main.c new file mode 100644 index 0000000..902e908 --- /dev/null +++ b/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/unit-tests/test-cases/tentative-to-real/Makefile b/unit-tests/test-cases/tentative-to-real/Makefile index 2115e7b..092aa2e 100644 --- a/unit-tests/test-cases/tentative-to-real/Makefile +++ b/unit-tests/test-cases/tentative-to-real/Makefile @@ -2,14 +2,14 @@ # Copyright (c) 2006 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ -# +# # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. -# +# # The Original Code and all software distributed under the 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,7 +17,7 @@ # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. -# +# # @APPLE_LICENSE_HEADER_END@ ## TESTROOT = ../.. @@ -33,12 +33,13 @@ 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 - ${OBJECTDUMP} test-r.${ARCH}.o | grep tentative | ${PASS_IFF_STDIN} + ${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 - ${OBJECTDUMP} test-r-d.${ARCH}.o | grep tentative | ${PASS_IFF_EMPTY} + ${FAIL_IF_ERROR} ${OBJECTDUMP} test-r-d.${ARCH}.o | grep tentative | ${PASS_IFF_EMPTY} clean: - rm -rf test.${ARCH}.o test-r.${ARCH}.o - - + rm -rf *.o diff --git a/unit-tests/test-cases/tentative-to-real/comment.txt b/unit-tests/test-cases/tentative-to-real/comment.txt new file mode 100644 index 0000000..de80bea --- /dev/null +++ b/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/unit-tests/test-cases/undefined-dynamic-lookup/Makefile b/unit-tests/test-cases/undefined-dynamic-lookup/Makefile new file mode 100644 index 0000000..eebcc37 --- /dev/null +++ b/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/unit-tests/test-cases/undefined-dynamic-lookup/main.c b/unit-tests/test-cases/undefined-dynamic-lookup/main.c new file mode 100644 index 0000000..5de972f --- /dev/null +++ b/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/unit-tests/test-cases/weak-def-ordinal/Makefile b/unit-tests/test-cases/weak-def-ordinal/Makefile new file mode 100644 index 0000000..222a82d --- /dev/null +++ b/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/unit-tests/test-cases/weak-def-ordinal/bar.c b/unit-tests/test-cases/weak-def-ordinal/bar.c new file mode 100644 index 0000000..ae61731 --- /dev/null +++ b/unit-tests/test-cases/weak-def-ordinal/bar.c @@ -0,0 +1,6 @@ + +int aaa() +{ + return 1; +} + diff --git a/unit-tests/test-cases/weak-def-ordinal/foo.c b/unit-tests/test-cases/weak-def-ordinal/foo.c new file mode 100644 index 0000000..d371654 --- /dev/null +++ b/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/unit-tests/test-cases/weak-def-ordinal/main.c b/unit-tests/test-cases/weak-def-ordinal/main.c new file mode 100644 index 0000000..74726fb --- /dev/null +++ b/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/unit-tests/test-cases/weak_import/Makefile b/unit-tests/test-cases/weak_import/Makefile index 69ff397..d1fa1f3 100644 --- a/unit-tests/test-cases/weak_import/Makefile +++ b/unit-tests/test-cases/weak_import/Makefile @@ -32,6 +32,8 @@ 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 @@ -41,6 +43,9 @@ 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 + ${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 @@ -50,7 +55,8 @@ 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 ${PASS_IFF_GOOD_MACHO} main-${ARCH}.dylib clean: - rm -rf libfoo-${ARCH}.dylib main-${ARCH} main-${ARCH}.dylib + rm -rf *.dylib main-* diff --git a/unit-tests/test-cases/weak_import2/Makefile.newtest b/unit-tests/test-cases/weak_import2/Makefile.newtest new file mode 100644 index 0000000..5e51b89 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import2/comment.txt b/unit-tests/test-cases/weak_import2/comment.txt new file mode 100644 index 0000000..5be42d8 --- /dev/null +++ b/unit-tests/test-cases/weak_import2/comment.txt @@ -0,0 +1 @@ +Test the weak_import attribute works diff --git a/unit-tests/test-cases/weak_import2/foo.c b/unit-tests/test-cases/weak_import2/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import2/foo.h b/unit-tests/test-cases/weak_import2/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import2/foo1.c b/unit-tests/test-cases/weak_import2/foo1.c new file mode 100644 index 0000000..4580a87 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import2/main.c b/unit-tests/test-cases/weak_import2/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/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/unit-tests/test-cases/weak_import3/Makefile b/unit-tests/test-cases/weak_import3/Makefile new file mode 100644 index 0000000..98a2779 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import3/comment.txt b/unit-tests/test-cases/weak_import3/comment.txt new file mode 100644 index 0000000..5be42d8 --- /dev/null +++ b/unit-tests/test-cases/weak_import3/comment.txt @@ -0,0 +1 @@ +Test the weak_import attribute works diff --git a/unit-tests/test-cases/weak_import3/foo.c b/unit-tests/test-cases/weak_import3/foo.c new file mode 100644 index 0000000..900b052 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import3/foo.h b/unit-tests/test-cases/weak_import3/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import3/foo1.c b/unit-tests/test-cases/weak_import3/foo1.c new file mode 100644 index 0000000..392a5b7 --- /dev/null +++ b/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/unit-tests/test-cases/weak_import3/main.c b/unit-tests/test-cases/weak_import3/main.c new file mode 100644 index 0000000..3266aed --- /dev/null +++ b/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/unit-tests/test-cases/zero-fill/Makefile b/unit-tests/test-cases/zero-fill/Makefile index a5f2f12..d6c0639 100644 --- a/unit-tests/test-cases/zero-fill/Makefile +++ b/unit-tests/test-cases/zero-fill/Makefile @@ -35,6 +35,4 @@ all: ${PASS_IFF_GOOD_MACHO} test-${ARCH} clean: - rm test-${ARCH} - - + rm -rf test-* diff --git a/unit-tests/test-cases/zero-fill2/Makefile b/unit-tests/test-cases/zero-fill2/Makefile new file mode 100644 index 0000000..433078b --- /dev/null +++ b/unit-tests/test-cases/zero-fill2/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 +# can link a program with a large zero-fill section +# + +run: all + +all: + BOOST=1; \ + [ X"${ARCH}" = Xppc64 -o X"${ARCH}" = Xx86_64 ] && BOOST=100L; \ + ${CC} ${CCFLAGS} -DBOOST=$$BOOST test.c -mmacosx-version-min=10.4 -o test-${ARCH} -L/Developer/SDKs/MacOSX10.4u.sdk/usr/lib -L/Developer/SDKs/MacOSX10.4u.sdk/usr/lib/system + + ${PASS_IFF_GOOD_MACHO} test-${ARCH} + +clean: + rm -rf test-* diff --git a/unit-tests/test-cases/zero-fill2/comment.txt b/unit-tests/test-cases/zero-fill2/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/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/unit-tests/test-cases/zero-fill2/test.c b/unit-tests/test-cases/zero-fill2/test.c new file mode 100644 index 0000000..74144d0 --- /dev/null +++ b/unit-tests/test-cases/zero-fill2/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[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/unit-tests/test-cases/zero-fill3/Makefile b/unit-tests/test-cases/zero-fill3/Makefile new file mode 100644 index 0000000..50399b7 --- /dev/null +++ b/unit-tests/test-cases/zero-fill3/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 +# 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 + +clean: + rm -rf test-* *.o *.s *.i diff --git a/unit-tests/test-cases/zero-fill3/comment.txt b/unit-tests/test-cases/zero-fill3/comment.txt new file mode 100644 index 0000000..a1710c1 --- /dev/null +++ b/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/unit-tests/test-cases/zero-fill3/test.c b/unit-tests/test-cases/zero-fill3/test.c new file mode 100644 index 0000000..64ac72a --- /dev/null +++ b/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; +} -- 2.45.2