From a645023da60d22e86be13f7b4d97adeff8bc6665 Mon Sep 17 00:00:00 2001 From: Apple <opensource@apple.com> Date: Fri, 10 Dec 2010 22:41:27 +0000 Subject: [PATCH] ld64-123.2.tar.gz --- ChangeLog | 5227 +------ doc/man/man1/dyldinfo.1 | 10 +- doc/man/man1/ld.1 | 105 +- ld64.xcodeproj/project.pbxproj | 507 +- src/abstraction/FileAbstraction.hpp | 4 +- src/abstraction/MachOFileAbstraction.hpp | 104 +- src/abstraction/MachOTrie.hpp | 126 +- src/ld/Architectures.hpp | 34 +- src/ld/ArchiveReader.hpp | 467 - src/ld/ExecutableFile.h | 75 - src/ld/HeaderAndLoadCommands.hpp | 1391 ++ src/ld/InputFiles.cpp | 943 ++ src/ld/InputFiles.h | 115 + src/ld/LTOReader.hpp | 742 - src/ld/LinkEdit.hpp | 1306 ++ src/ld/LinkEditClassic.hpp | 2542 ++++ src/ld/MachOReaderDylib.hpp | 1048 -- src/ld/MachOReaderRelocatable.hpp | 6125 -------- src/ld/MachOWriterExecutable.hpp | 12114 ---------------- src/ld/ObjectFile.h | 385 - src/ld/OpaqueSection.hpp | 199 - src/ld/Options.cpp | 1297 +- src/ld/Options.h | 323 +- src/ld/OutputFile.cpp | 3503 +++++ src/ld/OutputFile.h | 289 + src/ld/Resolver.cpp | 1402 ++ src/ld/Resolver.h | 148 + src/ld/SymbolTable.cpp | 721 + src/ld/SymbolTable.h | 165 + src/ld/debugline.c | 44 +- src/ld/debugline.h | 7 +- src/ld/ld.cpp | 4612 +----- src/ld/ld.hpp | 710 + src/ld/lto_file.hpp | 642 + src/ld/parsers/archive_file.cpp | 522 + src/ld/parsers/archive_file.h | 48 + src/ld/parsers/lto_file.cpp | 870 ++ src/ld/parsers/lto_file.h | 73 + src/ld/parsers/macho_dylib_file.cpp | 996 ++ src/ld/parsers/macho_dylib_file.h | 41 + src/ld/parsers/macho_relocatable_file.cpp | 6552 +++++++++ src/ld/parsers/macho_relocatable_file.h | 58 + src/ld/parsers/opaque_section_file.cpp | 106 + src/ld/parsers/opaque_section_file.h | 46 + src/ld/passes/branch_island.cpp | 623 + src/ld/passes/branch_island.h | 45 + src/ld/passes/branch_shim.cpp | 314 + src/ld/passes/branch_shim.h | 45 + src/ld/passes/compact_unwind.cpp | 794 + src/ld/passes/compact_unwind.h | 45 + src/ld/passes/dtrace_dof.cpp | 339 + src/ld/passes/dtrace_dof.h | 45 + src/ld/passes/dylibs.cpp | 86 + src/ld/passes/dylibs.h | 45 + src/ld/passes/got.cpp | 276 + src/ld/passes/got.h | 45 + src/ld/passes/huge.cpp | 113 + src/ld/passes/huge.h | 45 + src/ld/passes/objc.cpp | 1174 ++ src/ld/passes/objc.h | 45 + src/ld/passes/order_file.cpp | 531 + src/ld/passes/order_file.h | 45 + src/ld/passes/stubs/make_stubs.h | 39 + src/ld/passes/stubs/stub_arm.hpp | 416 + src/ld/passes/stubs/stub_arm_classic.hpp | 155 + src/ld/passes/stubs/stub_ppc_classic.hpp | 190 + src/ld/passes/stubs/stub_x86.hpp | 347 + src/ld/passes/stubs/stub_x86_64.hpp | 366 + src/ld/passes/stubs/stub_x86_64_classic.hpp | 165 + src/ld/passes/stubs/stub_x86_classic.hpp | 163 + src/ld/passes/stubs/stubs.cpp | 385 + src/ld/passes/tlvp.cpp | 303 + src/ld/passes/tlvp.h | 45 + src/other/ObjectDump.cpp | 888 +- src/other/dyldinfo.cpp | 390 +- src/other/machochecker.cpp | 187 +- src/other/rebase.cpp | 28 +- src/other/unwinddump.cpp | 54 +- unit-tests/bin/result-filter.pl | 18 + unit-tests/include/common.makefile | 28 +- unit-tests/run-all-unit-tests | 1 + .../test-cases/16-byte-alignment/Makefile | 2 +- unit-tests/test-cases/Lpath/Makefile | 43 + unit-tests/test-cases/Lpath/foo.c | 2 + unit-tests/test-cases/Lpath/main.c | 8 + unit-tests/test-cases/absolute-symbol/abs.s | 6 +- .../test-cases/alias-command-line/Makefile | 4 +- .../test-cases/alias-command-line/aliases.s | 7 + .../test-cases/alias-command-line/aliases.txt | 1 + .../test-cases/allow_heap_execute/Makefile | 35 + .../test-cases/allow_heap_execute/main.c | 4 + .../archive-ObjC-unexported/Makefile | 44 + .../test-cases/archive-ObjC-unexported/bar.m | 14 + .../test-cases/archive-ObjC-unexported/foo.m | 8 + .../test-cases/archive-ObjC-unexported/main.m | 9 + .../archive-ObjC-unexported/main.nexp | 1 + .../test-cases/archive-image_info/Makefile | 50 + .../test-cases/archive-image_info/main.m | 36 + unit-tests/test-cases/bind_at_load/Makefile | 41 + unit-tests/test-cases/bind_at_load/bar.c | 2 + unit-tests/test-cases/bind_at_load/foo.c | 3 + unit-tests/test-cases/bind_at_load/main.c | 14 + unit-tests/test-cases/blank-stubs/Makefile | 4 +- .../test-cases/branch-distance/Makefile | 4 +- .../test-cases/branch-interworking/Makefile | 42 + .../test-cases/branch-interworking/myarm.s | 19 + .../test-cases/branch-interworking/mythumb.s | 20 + .../test-cases/cfstring-and-cstring/Makefile | 14 + .../test-cases/cfstring-and-cstring/bar.c | 14 + .../test-cases/cfstring-and-cstring/foo.c | 12 + unit-tests/test-cases/cfstring-utf16/Makefile | 4 +- .../coalesce_weak_def_in_dylib/Makefile | 2 +- unit-tests/test-cases/commons-order/Makefile | 2 +- .../test-cases/cstring-alt-segment/Makefile | 1 + .../test-cases/cstring-alt-segment/custom.s | 4 +- .../test-cases/cstring-empty-labeled/Makefile | 43 + .../test-cases/cstring-empty-labeled/foo.s | 21 + .../test-cases/custom-segment-layout/Makefile | 16 + .../test-cases/custom-segment-layout/main.c | 8 + .../test-cases/custom-segment-layout/zero.s | 11 + .../dead_strip-archive-duplicate-def/Makefile | 43 + .../dead_strip-archive-duplicate-def/bar.c | 8 + .../dead_strip-archive-duplicate-def/foo.c | 10 + .../dead_strip-archive-duplicate-def/main.c | 14 + .../dead_strip-archive-weak-override/Makefile | 43 + .../dead_strip-archive-weak-override/bar.c | 6 + .../dead_strip-archive-weak-override/foo.c | 15 + .../dead_strip-archive-weak-override/main.c | 25 + unit-tests/test-cases/dead_strip/Makefile | 3 +- unit-tests/test-cases/demangle/Makefile | 45 + unit-tests/test-cases/demangle/main.cxx | 42 + .../test-cases/dependency-logging/Makefile | 46 + .../test-cases/dependency-logging/foo.c | 1 + .../test-cases/dependency-logging/main.c | 7 + .../test-cases/dtrace-old-probes/Makefile | 36 + .../test-cases/dtrace-old-probes/main.c | 49 + .../dtrace-static-probes-coalescing/x.cxx | 2 +- .../test-cases/dtrace-static-probes/Makefile | 2 +- .../test-cases/dwarf-debug-notes-r/Makefile | 33 +- .../dwarf-debug-notes-uuid/Makefile | 45 + .../test-cases/dwarf-debug-notes-uuid/main.c | 23 + .../test-cases/dwarf-debug-notes/Makefile | 24 +- .../dwarf-debug-notes/expected-stabs | 6 +- .../test-cases/dwarf-strip-objc/Makefile | 29 + .../test-cases/dwarf-strip-objc/hello.m | 8 + unit-tests/test-cases/dwarf-strip/Makefile | 10 +- unit-tests/test-cases/dylib-aliases/Makefile | 2 +- .../test-cases/dylib-re-export-cycle/Makefile | 17 +- unit-tests/test-cases/dylib-upward/Makefile | 43 + unit-tests/test-cases/dylib-upward/bar.c | 1 + unit-tests/test-cases/dylib-upward/foo.c | 6 + unit-tests/test-cases/efi-basic/LibTest.c | 13 + unit-tests/test-cases/efi-basic/Makefile | 73 + unit-tests/test-cases/efi-basic/MtocTest.c | 21 + unit-tests/test-cases/efi-basic/mtoctest.py | 135 + unit-tests/test-cases/eh-coalescing/bar.cxx | 2 +- .../test-cases/eh-stripped-symbols/Makefile | 8 +- .../test-cases/eh-stripped-symbols/main.cxx | 51 +- unit-tests/test-cases/empty-dylib/Makefile | 14 +- .../exported-symbols-dead_strip/Makefile | 39 + .../exported-symbols-dead_strip/foo.c | 34 + .../exported-symbols-dead_strip/foo.exp | 3 + .../test-cases/function-starts/Makefile | 29 + unit-tests/test-cases/function-starts/main.c | 10 + unit-tests/test-cases/hidden-r/Makefile | 41 + unit-tests/test-cases/hidden-r/foo.c | 5 + unit-tests/test-cases/hidden-r/main.c | 12 + unit-tests/test-cases/kext-basic/mykext.c | 2 + .../label-on-end-of-section/Makefile | 2 +- unit-tests/test-cases/large-bss/Makefile | 38 + unit-tests/test-cases/large-bss/test.s | 33 + .../test-cases/lazy-dylib-objc/Makefile | 2 +- unit-tests/test-cases/lazy-dylib/Makefile | 4 +- .../test-cases/literals-labels/Makefile | 21 + .../test-cases/literals-labels/literals.s | 77 + .../test-cases/llvm-integration/Makefile | 10 +- unit-tests/test-cases/llvm-integration/a17.c | 8 + .../test-cases/lto-dead_strip-objc/Makefile | 41 + .../test-cases/lto-dead_strip-objc/foo.m | 27 + .../lto-dead_strip-some-hidden/Makefile | 41 + .../lto-dead_strip-some-hidden/bar.c | 19 + .../lto-dead_strip-tentative/Makefile | 44 + .../test-cases/lto-dead_strip-tentative/bar.c | 4 + .../test-cases/lto-dead_strip-tentative/baz.c | 5 + .../test-cases/lto-dead_strip-tentative/foo.c | 4 + .../lto-dead_strip-tentative/main.c | 12 + .../test-cases/lto-dead_strip-unused/Makefile | 43 + .../test-cases/lto-dead_strip-unused/bar.c | 15 + .../test-cases/lto-dead_strip-unused/main.c | 8 + .../test-cases/lto-objc-image-info/Makefile | 53 + .../test-cases/lto-objc-image-info/main.m | 9 + .../test-cases/lto-object_path/Makefile | 42 + unit-tests/test-cases/lto-object_path/main.c | 15 + unit-tests/test-cases/main-stripped/Makefile | 12 +- unit-tests/test-cases/main-stripped/main.c | 5 +- unit-tests/test-cases/main-stripped/main.exp | 1 + unit-tests/test-cases/no-uuid/Makefile | 4 +- unit-tests/test-cases/non-lazy-r/Makefile | 19 +- unit-tests/test-cases/non-lazy-r/foo.c | 3 + unit-tests/test-cases/non-lazy-r/other.c | 1 + .../test-cases/non-lazy-sections-r/Makefile | 55 + .../test-cases/non-lazy-sections-r/foo.s | 34 + unit-tests/test-cases/objc-abi/Makefile | 49 + unit-tests/test-cases/objc-abi/test.m | 17 + .../test-cases/objc-category-archive/Makefile | 27 + .../test-cases/objc-category-archive/main.m | 6 + .../test-cases/objc-category-archive/test.m | 21 + .../objc-category-optimize-load/Makefile | 52 + .../objc-category-optimize-load/cat1.m | 15 + .../objc-category-optimize-load/foo.m | 9 + .../objc-category-optimize/Makefile | 71 + .../test-cases/objc-category-optimize/cat1.m | 29 + .../test-cases/objc-category-optimize/cat2.m | 37 + .../test-cases/objc-category-optimize/foo.m | 17 + .../test-cases/objc-class-alias/Makefile | 44 + unit-tests/test-cases/objc-class-alias/test.m | 13 + unit-tests/test-cases/objc-gc-checks/Makefile | 52 +- unit-tests/test-cases/objc-gc-checks/none.c | 1 + .../test-cases/objc-literal-pointers/Makefile | 4 +- .../test-cases/objc-properties/Makefile | 19 + unit-tests/test-cases/objc-properties/test.m | 49 + .../test-cases/objc-visibility/Makefile | 42 + unit-tests/test-cases/objc-visibility/bar.h | 8 + unit-tests/test-cases/objc-visibility/bar.m | 13 + unit-tests/test-cases/objc-visibility/foo.h | 8 + unit-tests/test-cases/objc-visibility/foo.m | 14 + unit-tests/test-cases/order_file-ans/main.cxx | 6 +- unit-tests/test-cases/order_file/Makefile | 16 +- unit-tests/test-cases/order_file/extra.s | 8 +- .../test-cases/order_file/main4.expected | 6 + unit-tests/test-cases/order_file/main4.order | 4 + .../test-cases/re-export-and-use/Makefile | 54 + unit-tests/test-cases/re-export-and-use/bar.c | 5 + unit-tests/test-cases/re-export-and-use/baz.c | 5 + unit-tests/test-cases/re-export-and-use/foo.c | 16 + unit-tests/test-cases/re-export-and-use/pub.c | 5 + .../test-cases/re-export-layers/Makefile | 61 + unit-tests/test-cases/re-export-layers/bar.c | 5 + unit-tests/test-cases/re-export-layers/baz.c | 5 + unit-tests/test-cases/re-export-layers/foo.c | 4 + unit-tests/test-cases/re-export-layers/main.c | 15 + .../test-cases/re-export-symbol/Makefile | 56 + unit-tests/test-cases/re-export-symbol/bar.c | 5 + unit-tests/test-cases/re-export-symbol/foo.c | 4 + .../test-cases/re-export-symbol/foo.exp | 2 + .../test-cases/re-export-symbol/foo2.exp | 2 + .../test-cases/re-export-symbol/main1.c | 9 + .../test-cases/re-export-symbol/main2.c | 10 + .../test-cases/read-only-relocs/Makefile | 44 +- unit-tests/test-cases/read-only-relocs/main.c | 10 + .../test-cases/read-only-relocs/test_bind.c | 10 + .../test-cases/read-only-relocs/test_rebase.c | 8 + unit-tests/test-cases/rebase-basic/Makefile | 6 +- .../test-cases/reexport_symbols_list/Makefile | 61 + .../test-cases/reexport_symbols_list/bar.c | 10 + .../test-cases/reexport_symbols_list/bart.exp | 1 + .../test-cases/reexport_symbols_list/foo.c | 4 + .../test-cases/reexport_symbols_list/foo.exp | 1 + .../test-cases/reexport_symbols_list/junk.exp | 1 + .../test-cases/reexport_symbols_list/main1.c | 9 + unit-tests/test-cases/relocs-asm/relocs-asm.s | 171 +- .../test-cases/relocs-neg-from-local/Makefile | 6 +- .../test-cases/sectcreate-dead_strip/Makefile | 40 + .../test-cases/sectcreate-dead_strip/main.c | 5 + .../sectcreate-dead_strip/sect_content | 1 + unit-tests/test-cases/segment-labels/Makefile | 40 + unit-tests/test-cases/segment-labels/main.c | 50 + unit-tests/test-cases/segment-labels/test.c | 12 + unit-tests/test-cases/stabs-coalesce/Makefile | 6 +- .../static-executable-weak-defines/Makefile | 3 +- .../static-executable-weak-defines/test.c | 3 + .../test-cases/static-executable/test.c | 7 +- .../symbol-hiding-umbrella/Makefile | 48 + .../test-cases/symbol-hiding-umbrella/bar.c | 1 + .../test-cases/symbol-hiding-umbrella/foo.c | 7 + .../test-cases/symbol-hiding-umbrella/main.c | 11 + .../test-cases/symbol-resolver-basic/Makefile | 29 + .../test-cases/symbol-resolver-basic/foo.c | 38 + .../symbol-resolver-hidden/Makefile | 25 + .../test-cases/symbol-resolver-hidden/foo.c | 42 + .../test-cases/tentative-to-real-r/Makefile | 4 +- unit-tests/test-cases/tlv-basic/Makefile | 39 + unit-tests/test-cases/tlv-basic/get.s | 162 + unit-tests/test-cases/tlv-basic/main.c | 39 + unit-tests/test-cases/tlv-dylib/Makefile | 46 + unit-tests/test-cases/tlv-dylib/foo.s | 33 + unit-tests/test-cases/tlv-dylib/getbar.s | 29 + unit-tests/test-cases/tlv-dylib/getfoo.s | 29 + .../test-cases/tlv-dylib/main.c | 27 +- unit-tests/test-cases/umbrella-dylib/Makefile | 54 + unit-tests/test-cases/umbrella-dylib/a.c | 11 + unit-tests/test-cases/umbrella-dylib/b.c | 7 + unit-tests/test-cases/umbrella-dylib/c.c | 11 + unit-tests/test-cases/umbrella-dylib/main.c | 8 + .../undefined-dynamic-lookup/Makefile | 21 +- .../test-cases/unstrippable-symbols/Makefile | 17 + .../test-cases/unstrippable-symbols/foo.c | 24 + unit-tests/test-cases/utf16-nul/Makefile | 41 + unit-tests/test-cases/utf16-nul/other.s | 12 + unit-tests/test-cases/utf16-nul/withnul.s | 8 + .../test-cases/visibility-warning/Makefile | 32 +- .../test-cases/weak-def-auto-hide/Makefile | 57 + .../test-cases/weak-def-auto-hide/main.c | 15 + .../test-cases/weak-def-auto-hide/other.s | 15 + unit-tests/test-cases/weak-def-flag/Makefile | 2 +- unit-tests/test-cases/weak-force/Makefile | 50 + unit-tests/test-cases/weak-force/foo.c | 17 + unit-tests/test-cases/weak-force/foo1.exp | 2 + unit-tests/test-cases/weak-force/foo2.exp | 2 + .../test-cases/weak_import-force/main.c | 13 +- .../test-cases/weak_import-local/Makefile | 40 + unit-tests/test-cases/weak_import-local/foo.c | 7 + unit-tests/test-cases/weak_import-local/foo.h | 6 + .../test-cases/weak_import-local/main.c | 18 + unit-tests/test-cases/weak_import/main.c | 7 +- unit-tests/test-cases/zero-fill2/test.c | 7 +- 316 files changed, 39598 insertions(+), 30933 deletions(-) delete mode 100644 src/ld/ArchiveReader.hpp delete mode 100644 src/ld/ExecutableFile.h create mode 100644 src/ld/HeaderAndLoadCommands.hpp create mode 100644 src/ld/InputFiles.cpp create mode 100644 src/ld/InputFiles.h delete mode 100644 src/ld/LTOReader.hpp create mode 100644 src/ld/LinkEdit.hpp create mode 100644 src/ld/LinkEditClassic.hpp delete mode 100644 src/ld/MachOReaderDylib.hpp delete mode 100644 src/ld/MachOReaderRelocatable.hpp delete mode 100644 src/ld/MachOWriterExecutable.hpp delete mode 100644 src/ld/ObjectFile.h delete mode 100644 src/ld/OpaqueSection.hpp create mode 100644 src/ld/OutputFile.cpp create mode 100644 src/ld/OutputFile.h create mode 100644 src/ld/Resolver.cpp create mode 100644 src/ld/Resolver.h create mode 100644 src/ld/SymbolTable.cpp create mode 100644 src/ld/SymbolTable.h create mode 100644 src/ld/ld.hpp create mode 100644 src/ld/lto_file.hpp create mode 100644 src/ld/parsers/archive_file.cpp create mode 100644 src/ld/parsers/archive_file.h create mode 100644 src/ld/parsers/lto_file.cpp create mode 100644 src/ld/parsers/lto_file.h create mode 100644 src/ld/parsers/macho_dylib_file.cpp create mode 100644 src/ld/parsers/macho_dylib_file.h create mode 100644 src/ld/parsers/macho_relocatable_file.cpp create mode 100644 src/ld/parsers/macho_relocatable_file.h create mode 100644 src/ld/parsers/opaque_section_file.cpp create mode 100644 src/ld/parsers/opaque_section_file.h create mode 100644 src/ld/passes/branch_island.cpp create mode 100644 src/ld/passes/branch_island.h create mode 100644 src/ld/passes/branch_shim.cpp create mode 100644 src/ld/passes/branch_shim.h create mode 100644 src/ld/passes/compact_unwind.cpp create mode 100644 src/ld/passes/compact_unwind.h create mode 100644 src/ld/passes/dtrace_dof.cpp create mode 100644 src/ld/passes/dtrace_dof.h create mode 100644 src/ld/passes/dylibs.cpp create mode 100644 src/ld/passes/dylibs.h create mode 100644 src/ld/passes/got.cpp create mode 100644 src/ld/passes/got.h create mode 100644 src/ld/passes/huge.cpp create mode 100644 src/ld/passes/huge.h create mode 100644 src/ld/passes/objc.cpp create mode 100644 src/ld/passes/objc.h create mode 100644 src/ld/passes/order_file.cpp create mode 100644 src/ld/passes/order_file.h create mode 100644 src/ld/passes/stubs/make_stubs.h create mode 100644 src/ld/passes/stubs/stub_arm.hpp create mode 100644 src/ld/passes/stubs/stub_arm_classic.hpp create mode 100644 src/ld/passes/stubs/stub_ppc_classic.hpp create mode 100644 src/ld/passes/stubs/stub_x86.hpp create mode 100644 src/ld/passes/stubs/stub_x86_64.hpp create mode 100644 src/ld/passes/stubs/stub_x86_64_classic.hpp create mode 100644 src/ld/passes/stubs/stub_x86_classic.hpp create mode 100644 src/ld/passes/stubs/stubs.cpp create mode 100644 src/ld/passes/tlvp.cpp create mode 100644 src/ld/passes/tlvp.h create mode 100644 unit-tests/test-cases/Lpath/Makefile create mode 100644 unit-tests/test-cases/Lpath/foo.c create mode 100644 unit-tests/test-cases/Lpath/main.c create mode 100644 unit-tests/test-cases/allow_heap_execute/Makefile create mode 100644 unit-tests/test-cases/allow_heap_execute/main.c create mode 100644 unit-tests/test-cases/archive-ObjC-unexported/Makefile create mode 100644 unit-tests/test-cases/archive-ObjC-unexported/bar.m create mode 100644 unit-tests/test-cases/archive-ObjC-unexported/foo.m create mode 100644 unit-tests/test-cases/archive-ObjC-unexported/main.m create mode 100644 unit-tests/test-cases/archive-ObjC-unexported/main.nexp create mode 100644 unit-tests/test-cases/archive-image_info/Makefile create mode 100644 unit-tests/test-cases/archive-image_info/main.m create mode 100644 unit-tests/test-cases/bind_at_load/Makefile create mode 100644 unit-tests/test-cases/bind_at_load/bar.c create mode 100644 unit-tests/test-cases/bind_at_load/foo.c create mode 100644 unit-tests/test-cases/bind_at_load/main.c create mode 100644 unit-tests/test-cases/branch-interworking/Makefile create mode 100644 unit-tests/test-cases/branch-interworking/myarm.s create mode 100644 unit-tests/test-cases/branch-interworking/mythumb.s create mode 100644 unit-tests/test-cases/cfstring-and-cstring/Makefile create mode 100644 unit-tests/test-cases/cfstring-and-cstring/bar.c create mode 100644 unit-tests/test-cases/cfstring-and-cstring/foo.c create mode 100644 unit-tests/test-cases/cstring-empty-labeled/Makefile create mode 100644 unit-tests/test-cases/cstring-empty-labeled/foo.s create mode 100644 unit-tests/test-cases/custom-segment-layout/Makefile create mode 100644 unit-tests/test-cases/custom-segment-layout/main.c create mode 100644 unit-tests/test-cases/custom-segment-layout/zero.s create mode 100644 unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile create mode 100644 unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c create mode 100644 unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c create mode 100644 unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c create mode 100644 unit-tests/test-cases/dead_strip-archive-weak-override/Makefile create mode 100644 unit-tests/test-cases/dead_strip-archive-weak-override/bar.c create mode 100644 unit-tests/test-cases/dead_strip-archive-weak-override/foo.c create mode 100644 unit-tests/test-cases/dead_strip-archive-weak-override/main.c create mode 100644 unit-tests/test-cases/demangle/Makefile create mode 100644 unit-tests/test-cases/demangle/main.cxx create mode 100644 unit-tests/test-cases/dependency-logging/Makefile create mode 100644 unit-tests/test-cases/dependency-logging/foo.c create mode 100644 unit-tests/test-cases/dependency-logging/main.c create mode 100644 unit-tests/test-cases/dtrace-old-probes/Makefile create mode 100644 unit-tests/test-cases/dtrace-old-probes/main.c create mode 100644 unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile create mode 100644 unit-tests/test-cases/dwarf-debug-notes-uuid/main.c create mode 100644 unit-tests/test-cases/dwarf-strip-objc/Makefile create mode 100644 unit-tests/test-cases/dwarf-strip-objc/hello.m create mode 100644 unit-tests/test-cases/dylib-upward/Makefile create mode 100644 unit-tests/test-cases/dylib-upward/bar.c create mode 100644 unit-tests/test-cases/dylib-upward/foo.c create mode 100644 unit-tests/test-cases/efi-basic/LibTest.c create mode 100644 unit-tests/test-cases/efi-basic/Makefile create mode 100644 unit-tests/test-cases/efi-basic/MtocTest.c create mode 100755 unit-tests/test-cases/efi-basic/mtoctest.py create mode 100644 unit-tests/test-cases/exported-symbols-dead_strip/Makefile create mode 100644 unit-tests/test-cases/exported-symbols-dead_strip/foo.c create mode 100644 unit-tests/test-cases/exported-symbols-dead_strip/foo.exp create mode 100644 unit-tests/test-cases/function-starts/Makefile create mode 100644 unit-tests/test-cases/function-starts/main.c create mode 100644 unit-tests/test-cases/hidden-r/Makefile create mode 100644 unit-tests/test-cases/hidden-r/foo.c create mode 100644 unit-tests/test-cases/hidden-r/main.c create mode 100644 unit-tests/test-cases/large-bss/Makefile create mode 100644 unit-tests/test-cases/large-bss/test.s create mode 100644 unit-tests/test-cases/literals-labels/Makefile create mode 100644 unit-tests/test-cases/literals-labels/literals.s create mode 100644 unit-tests/test-cases/lto-dead_strip-objc/Makefile create mode 100644 unit-tests/test-cases/lto-dead_strip-objc/foo.m create mode 100644 unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile create mode 100644 unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c create mode 100644 unit-tests/test-cases/lto-dead_strip-tentative/Makefile create mode 100644 unit-tests/test-cases/lto-dead_strip-tentative/bar.c create mode 100644 unit-tests/test-cases/lto-dead_strip-tentative/baz.c create mode 100644 unit-tests/test-cases/lto-dead_strip-tentative/foo.c create mode 100644 unit-tests/test-cases/lto-dead_strip-tentative/main.c create mode 100644 unit-tests/test-cases/lto-dead_strip-unused/Makefile create mode 100644 unit-tests/test-cases/lto-dead_strip-unused/bar.c create mode 100644 unit-tests/test-cases/lto-dead_strip-unused/main.c create mode 100644 unit-tests/test-cases/lto-objc-image-info/Makefile create mode 100644 unit-tests/test-cases/lto-objc-image-info/main.m create mode 100644 unit-tests/test-cases/lto-object_path/Makefile create mode 100644 unit-tests/test-cases/lto-object_path/main.c create mode 100644 unit-tests/test-cases/non-lazy-sections-r/Makefile create mode 100644 unit-tests/test-cases/non-lazy-sections-r/foo.s create mode 100644 unit-tests/test-cases/objc-abi/Makefile create mode 100644 unit-tests/test-cases/objc-abi/test.m create mode 100644 unit-tests/test-cases/objc-category-archive/Makefile create mode 100644 unit-tests/test-cases/objc-category-archive/main.m create mode 100644 unit-tests/test-cases/objc-category-archive/test.m create mode 100644 unit-tests/test-cases/objc-category-optimize-load/Makefile create mode 100644 unit-tests/test-cases/objc-category-optimize-load/cat1.m create mode 100644 unit-tests/test-cases/objc-category-optimize-load/foo.m create mode 100644 unit-tests/test-cases/objc-category-optimize/Makefile create mode 100644 unit-tests/test-cases/objc-category-optimize/cat1.m create mode 100644 unit-tests/test-cases/objc-category-optimize/cat2.m create mode 100644 unit-tests/test-cases/objc-category-optimize/foo.m create mode 100644 unit-tests/test-cases/objc-class-alias/Makefile create mode 100644 unit-tests/test-cases/objc-class-alias/test.m create mode 100644 unit-tests/test-cases/objc-gc-checks/none.c create mode 100644 unit-tests/test-cases/objc-properties/Makefile create mode 100644 unit-tests/test-cases/objc-properties/test.m create mode 100644 unit-tests/test-cases/objc-visibility/Makefile create mode 100644 unit-tests/test-cases/objc-visibility/bar.h create mode 100644 unit-tests/test-cases/objc-visibility/bar.m create mode 100644 unit-tests/test-cases/objc-visibility/foo.h create mode 100644 unit-tests/test-cases/objc-visibility/foo.m create mode 100644 unit-tests/test-cases/order_file/main4.expected create mode 100644 unit-tests/test-cases/order_file/main4.order create mode 100644 unit-tests/test-cases/re-export-and-use/Makefile create mode 100644 unit-tests/test-cases/re-export-and-use/bar.c create mode 100644 unit-tests/test-cases/re-export-and-use/baz.c create mode 100644 unit-tests/test-cases/re-export-and-use/foo.c create mode 100644 unit-tests/test-cases/re-export-and-use/pub.c create mode 100644 unit-tests/test-cases/re-export-layers/Makefile create mode 100644 unit-tests/test-cases/re-export-layers/bar.c create mode 100644 unit-tests/test-cases/re-export-layers/baz.c create mode 100644 unit-tests/test-cases/re-export-layers/foo.c create mode 100644 unit-tests/test-cases/re-export-layers/main.c create mode 100644 unit-tests/test-cases/re-export-symbol/Makefile create mode 100644 unit-tests/test-cases/re-export-symbol/bar.c create mode 100644 unit-tests/test-cases/re-export-symbol/foo.c create mode 100644 unit-tests/test-cases/re-export-symbol/foo.exp create mode 100644 unit-tests/test-cases/re-export-symbol/foo2.exp create mode 100644 unit-tests/test-cases/re-export-symbol/main1.c create mode 100644 unit-tests/test-cases/re-export-symbol/main2.c create mode 100644 unit-tests/test-cases/read-only-relocs/main.c create mode 100644 unit-tests/test-cases/read-only-relocs/test_bind.c create mode 100644 unit-tests/test-cases/read-only-relocs/test_rebase.c create mode 100644 unit-tests/test-cases/reexport_symbols_list/Makefile create mode 100644 unit-tests/test-cases/reexport_symbols_list/bar.c create mode 100644 unit-tests/test-cases/reexport_symbols_list/bart.exp create mode 100644 unit-tests/test-cases/reexport_symbols_list/foo.c create mode 100644 unit-tests/test-cases/reexport_symbols_list/foo.exp create mode 100644 unit-tests/test-cases/reexport_symbols_list/junk.exp create mode 100644 unit-tests/test-cases/reexport_symbols_list/main1.c create mode 100644 unit-tests/test-cases/sectcreate-dead_strip/Makefile create mode 100644 unit-tests/test-cases/sectcreate-dead_strip/main.c create mode 100644 unit-tests/test-cases/sectcreate-dead_strip/sect_content create mode 100644 unit-tests/test-cases/segment-labels/Makefile create mode 100644 unit-tests/test-cases/segment-labels/main.c create mode 100644 unit-tests/test-cases/segment-labels/test.c create mode 100644 unit-tests/test-cases/symbol-hiding-umbrella/Makefile create mode 100644 unit-tests/test-cases/symbol-hiding-umbrella/bar.c create mode 100644 unit-tests/test-cases/symbol-hiding-umbrella/foo.c create mode 100644 unit-tests/test-cases/symbol-hiding-umbrella/main.c create mode 100644 unit-tests/test-cases/symbol-resolver-basic/Makefile create mode 100644 unit-tests/test-cases/symbol-resolver-basic/foo.c create mode 100644 unit-tests/test-cases/symbol-resolver-hidden/Makefile create mode 100644 unit-tests/test-cases/symbol-resolver-hidden/foo.c create mode 100644 unit-tests/test-cases/tlv-basic/Makefile create mode 100644 unit-tests/test-cases/tlv-basic/get.s create mode 100644 unit-tests/test-cases/tlv-basic/main.c create mode 100644 unit-tests/test-cases/tlv-dylib/Makefile create mode 100644 unit-tests/test-cases/tlv-dylib/foo.s create mode 100644 unit-tests/test-cases/tlv-dylib/getbar.s create mode 100644 unit-tests/test-cases/tlv-dylib/getfoo.s rename src/ld/SectCreate.h => unit-tests/test-cases/tlv-dylib/main.c (74%) create mode 100644 unit-tests/test-cases/umbrella-dylib/Makefile create mode 100644 unit-tests/test-cases/umbrella-dylib/a.c create mode 100644 unit-tests/test-cases/umbrella-dylib/b.c create mode 100644 unit-tests/test-cases/umbrella-dylib/c.c create mode 100644 unit-tests/test-cases/umbrella-dylib/main.c create mode 100644 unit-tests/test-cases/unstrippable-symbols/Makefile create mode 100644 unit-tests/test-cases/unstrippable-symbols/foo.c create mode 100644 unit-tests/test-cases/utf16-nul/Makefile create mode 100644 unit-tests/test-cases/utf16-nul/other.s create mode 100644 unit-tests/test-cases/utf16-nul/withnul.s create mode 100644 unit-tests/test-cases/weak-def-auto-hide/Makefile create mode 100644 unit-tests/test-cases/weak-def-auto-hide/main.c create mode 100644 unit-tests/test-cases/weak-def-auto-hide/other.s create mode 100644 unit-tests/test-cases/weak-force/Makefile create mode 100644 unit-tests/test-cases/weak-force/foo.c create mode 100644 unit-tests/test-cases/weak-force/foo1.exp create mode 100644 unit-tests/test-cases/weak-force/foo2.exp create mode 100644 unit-tests/test-cases/weak_import-local/Makefile create mode 100644 unit-tests/test-cases/weak_import-local/foo.c create mode 100644 unit-tests/test-cases/weak_import-local/foo.h create mode 100644 unit-tests/test-cases/weak_import-local/main.c diff --git a/ChangeLog b/ChangeLog index ebd5628..bc918bb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5177 +1,1198 @@ ------ Tagged ld64-97.17 +2010-12-10 Nick Kledzik <kledzik@apple.com> -2010-09-07 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8746980> Man page typo: "dysmutil" under object_path_lto - <rdar://problem/7989734> ld mis-handling std::tr1::anonymous symbols - Remove support for ordering gcc-4.0 compiled anonymous namespace symbols +2010-12-10 Nick Kledzik <kledzik@apple.com> + + <rdar://problem/8746896> ld64 crashes when warning about re-exported symbol -2010-09-07 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-123.1 - <rdar://problem/8388362> RedGarnet's linker does not honor $ld$hide for umbrella libraries +2010-12-07 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-97.16 + <rdar://problem/8732097> assertion if symbol from re-exported dylib is in -exported_symbols_list -2010-08-10 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-123 - <rdar://problem/8303976> add -demangle noop to ld64-97 +2010-12-06 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-97.15 + <rdar://problem/8649182> Change default search order and add -search_dylibs_first to restore old behavior -2010-08-10 Nick Kledzik <kledzik@apple.com> +2010-12-06 Nick Kledzik <kledzik@apple.com> - <rdar://problem/8212744> linker crash when using LTO in lto::Atom::getSymbolTableInclusion() + <rdar://problem/8626058> ld should consistently warn when resolvers are not exported ------ Tagged ld64-97.14 +2010-12-02 Nick Kledzik <kledzik@apple.com> -2010-04-20 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8723307> data segment has file offset 0 - <rdar://problem/7712869> if last section is zero-fill don't add size to filesize total in -r mode +-------- tagged ld64-122 ------ Tagged ld64-97.13 +2010-12-01 Nick Kledzik <kledzik@apple.com> -2010-03-17 Nick Kledzik <kledzik@apple.com> + Add #define ARM_RELOC_HALF in case trying to build with old mach-o/arm/reloc.h header - <rdar://problem/7824112> Support for iPhoneSimulator with OBJC 2.0 ABI +2010-11-30 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8134559> Linker should synthesize interworking stubs for tail calls + added test case: unit-tests/test-cases/branch-interworking ------ Tagged ld64-97.12 +2010-11-30 Nick Kledzik <kledzik@apple.com> -2010-03-17 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8708091> Link Time Optimization error with tentative defs and -dead_strip + added test case: unit-tests/test-cases/lto-dead_strip-tentative - <rdar://problem/7712869> if last section is zero-fill don't add size to filesize total in -r mode +-------- tagged ld64-121 ------ Tagged ld64-97.11 +2010-11-10 Nick Kledzik <kledzik@apple.com> -2010-01-26 Nick Kledzik <kledzik@apple.com> + Add -dylibs option to dyldinfo tool - <rdar://problem/7622634> Libc-624.1 causes latent ld bug - * build linker -no_pie - +2010-11-03 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-97.10 + <rdar://problem/7441442> Need support for ARM/thumb upper/lower 16 bit relocation -2010-01-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7556912> LC_SEGMENT command 0 filesize field greater than vmsize field - * Move __DATA to end in -r mode - +2010-11-03 Nick Kledzik <kledzik@apple.com> -2010-01-26 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8624334> Spelling typo in linker warning - <rdar://problem/7516793> symboled __ustring strings of just 0x0000 mis-parsed - * Parse __ustring section based on content - not just labels +2010-11-02 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8616906> Xcode 4: ld -r doesn't work on files compiled with llvm-gcc -flto ------ Tagged ld64-97.9 +2010-11-01 Nick Kledzik <kledzik@apple.com> -2010-01-14 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8618747> ld wrongly complaining about tlv relocs for i386 - <rdar://problem/7532743> LC_SEGMENT command 0 filesize field greater than vmsize field - * for i386 -r mode sort __IMPORT segment before __DATA segment +2010-11-01 Nick Kledzik <kledzik@apple.com> + Fix -why_live to list all references, not just first -2010-01-11 Nick Kledzik <kledzik@apple.com> +2010-11-01 Nick Kledzik <kledzik@apple.com> - * fix ARM -r -d references to tentative definitions + <rdar://problem/8612861> Durango is missing dof sections for armv7 slice ------ Tagged ld64-97.8 +-------- tagged ld64-120.3 -2009-12-08 Nick Kledzik <kledzik@apple.com> +2010-11-09 Nick Kledzik <kledzik@apple.com> - <rdar://problem/7455147> many mach-o images in Barolo have MH_WEAK_DEFINES bit incorrectly set - * don't let auto-strip weak symbols set MH_WEAK_DEFINES + <rdar://problem/8644314> revert default search order +-------- tagged ld64-120.2 ------ Tagged ld64-97.7 - -2009-11-30 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7429384> llvmgcc now puts const short arrays in __ustring section, linker does not handle that - * Only auto-coalesce UTF16 strings that are labeled with "___utf16_string*" - - ------ Tagged ld64-97.6 - -2009-11-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7332627> make -pie default for x86_64 for 10.7 and later - - -2009-10-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7331635> Add a -no_pie flag - * support -no_pie and LD_NO_PIE - +2010-11-09 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-97.5 + <rdar://problem/8510696> ld -r corrupts multiple non-lazy sections -2009-10-27 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-120.1 - <rdar://problem/7341117> crash when __ustring section has zero length string - * stop trying to suppress tailing 0x0000 from synthesized string used to coalese utf16 strings +2010-10-25 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zero data ------ Tagged ld64-97.4 - -2009-10-21 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7320293> missing thumb bit when thumb function takes address of itself - * move toao.atom == srcao.atom test to after fromao.atom == srcao.atom test - - ------ Tagged ld64-97.3 - -2009-10-06 Nick Kledzik <kledzik@apple.com> - - * Add missing LittleEndian::set32() in arm::kPointerDiff - -2009-10-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7268427> ARM: handle pointer-diff to weak thumb that is overridden by non-weak ARM - * When parsing ARM relocations, if target is a thumb function, remove one from addend - * When writing out content for arm::kPointerDiff, add one if target is thumb - * When writing ARM_RELOC_SECTDIFF, use target offsets if they fit in function - - ------ Tagged ld64-97.2 - -2009-09-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7200658> 'unknown DWARF EH encoding' message logged to console with clang-50 - - ------ Tagged ld64-97.1 - -2009-07-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7099040> make better no-PIC branch island to thumb1 - * Add kBranchIslandNoPicToThumb1 as 8-byte no-PIC island +-------- tagged ld64-120 +2010-10-25 Nick Kledzik <kledzik@apple.com> -2009-07-20 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8592564> ld should use this-image ordinal for symbols from re-exported non-public dylibs + added test case: unit-tests/test-cases/re-export-and-use - <rdar://problem/7075703> section$start$__DATA$__bss can fail - * In SectionBoundaryAtom constructor make __bss and __common be zero-fill - * In AtomSorter, make sure end kSectionEnd sort after kTentativeDefinition - * unit-tests/test-cases/section-labels-zero-fill: add test cases +2010-10-25 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8284718> Value of N_SO stabs should be address of first atom from translation unit ------ Tagged ld64-97 +2010-10-25 Nick Kledzik <kledzik@apple.com> -2009-07-13 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7049478> empty dylib should have subtype from command line - In Linker::writeOutput() for ARM, set initial fCurrentCpuConstraint to be command line subtype - - -2009-07-09 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7043920> crash when using -dead_strip and LTO with unresolved intrinsic - * In Linker::optimize() after final deadStripResolve() call checkUndefines() one last time - - -2009-07-09 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/7043256> ld64 can not find a -e entry point from an archive - * src/ld/ld.cpp: add searchArchives parameter to entryPoint() for use by deadStripResolve() - * unit-tests/test-cases/dead_strip-entry-archive: added test case + Always print arch name on undefined symbols error +2010-10-25 Nick Kledzik <kledzik@apple.com> -2009-07-08 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6702217> __objc_classrefs section could be coalesced - * In machoReader, chop up __objc_classrefs section into pointer size atoms - and make each weak and hidden with a name based on its target name. + Add ld64 version number to crash logs +2010-10-22 Nick Kledzik <kledzik@apple.com> -2009-06-26 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7952947> -objc_abi_version 1 not supported - <rdar://problem/6715874> "ld: symbol dyld_stub_binding_helper not defined" for xnu built with clang for x86_64 - * Fix relocationNeededInFinalLinkedImage() to say weak-defs don't require indirection in static executables - * Added unit-tests/test-cases/static-executable-weak-defines +2010-10-22 Nick Kledzik <kledzik@apple.com> + <rdar://problem/5591394> Add support to ld64 for N_FUN stabs when used for symbolic constants -2009-06-26 Nick Kledzik <kledzik@apple.com> +2010-10-22 Nick Kledzik <kledzik@apple.com> - <rdar://problem/7012016> Error msg for missing -init symbol is misleading/unclear - * In Linker::checkUndefines() check all ways that the command line could add an undefined - and then search for close matches/typos. + <rdar://problem/3560832> Change default search order and add -search_dylibs_first to restore old behavior +2010-10-22 Nick Kledzik <kledzik@apple.com> -2009-06-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5725900> We need ld-symbol-moving-symbols for ARM/Embedded - * In mach_o::dylib::Reader::addSymbol() parse iPhoneOS version strings - * Update IPhoneVersionMin - - -2009-06-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6933931> Linker makes subtype-zero file from empty subtype-nonzero file. - * In Linker::writeOutput() set inittal fCurrentCpuConstraint to be command line subtype - * Fix fArchitectureName to have arm sub-types + <rdar://problem/6955037> -L flag should support a space between it and its argument +2010-10-22 Nick Kledzik <kledzik@apple.com> -2009-06-24 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8553647> Hidden resolver functions don't work with DYLD_BIND_AT_LAUNCH - <rdar://problem/6955021> ld should do a better job of reporting attempts to link directories - * In Options::buildSearchPaths() sanity check -L and -F options - +2010-10-22 Nick Kledzik <kledzik@apple.com> -2009-06-24 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8541707> Support resolver functions for function pointer use from same linkage unit - <rdar://problem/6974647> better error message when libLTO.dylib not loadable - * in Linker::createReader() provide detail error messages +2010-10-19 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8571323> section$start$ does not always point to start of built in section types ------ Tagged ld64-96.10 +-------- tagged ld64-119.2 -2009-08-31 Nick Kledzik <kledzik@apple.com> +2010-10-18 Nick Kledzik <kledzik@apple.com> - <rdar://problem/7182988> empty dylib has been __mh_dylib_header n_sect - * rework patch to use MinimalTextAtom to ensure there is always a __text section - + <rdar://problem/8553312> make having an ObjC2 class symbol in an export list be a warning instead of an error ------ Tagged ld64-96.9 +2010-10-15 Nick Kledzik <kledzik@apple.com> -2009-08-31 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8553283> lazily produce (crt1.o missing) error - <rdar://problem/7182988> empty dylib has been __mh_dylib_header n_sect - * suppress __mh_dylib_header from symbol table if there is no __text section - - Implicitly add dyld_stub_binder to libSystem.dylib so iPhone clients can build - against SnowLeopard libSystem.dylib. +-------- tagged ld64-119.1 +2010-10-05 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-96.8 + <rdar://problem/8527740> ld -r can produce output with LC_DYLD_INFO load command -2009-08-30 Nick Kledzik <kledzik@apple.com> +2010-10-05 Nick Kledzik <kledzik@apple.com> - <rdar://problem/7049478> SWB: gcc-5577.1 fails to build vecLib - + <rdar://problem/8516692> ld doesn't pass stabs debug info through to the final executable any longer ------ Tagged ld64-96.7 +2010-10-05 Nick Kledzik <kledzik@apple.com> -2009-08-29 Nick Kledzik <kledzik@apple.com> + <rdar://problem/4251267> __UNIXSTACK placed incorrectly when -stack_addr < 0x4000000 - <rdar://problem/6933931> Linker makes subtype-zero file from empty subtype-nonzero file. +2010-10-05 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8267015> ld mishandles -lazy-l option + Updated test case: unit-tests/test-cases/lazy-dylib ------ Tagged ld64-96.6 - -2009-07-30 Nick Kledzik <kledzik@apple.com> +2010-10-04 Nick Kledzik <kledzik@apple.com> - <rdar://problem/7103437> ld: ldr 12-bit displacement out of range on SnowLeopard with gcc-5648 - * in Section::Section() set fIndex to ensure __symbolstub1 sorts to end of __TEXT segment + <rdar://problem/8468240> -no_compact_unwind should suppress dwarf->CUE warnings +-------- tagged ld64-119 ------ Tagged ld64-96.5 +2010-10-01 Nick Kledzik <kledzik@apple.com> -2009-07-28 Nick Kledzik <kledzik@apple.com> + <rdar://problem/6599016> use ld64 to link iBoot - <rdar://problem/7073626> Thumb mode compilation isn't working on 3.1 beta 2 - * Fix instructions used in kBranchIslandToThumb1 case +2010-10-01 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8504770> crash when entrypoint is thumb ------ Tagged ld64-96.4 +2010-10-01 Nick Kledzik <kledzik@apple.com> -2009-06-22 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6992387> platform linker should use platform ld_classic - * Fix Options::gotoClassicLinker() to use realpath()s - - -2009-06-22 Nick Kledzik <kledzik@apple.com> - - * Fix Options::setIPhoneVersionMin() to handle 2.x through 9.x + If -ios_version_min is used with -arch i386, assume simulator +2010-10-01 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-96.3 + <rdar://problem/8500061> crash with multiple re-exported dylibs with same install_name -2009-06-17 Nick Kledzik <kledzik@apple.com> +2010-09-28 Nick Kledzik <kledzik@apple.com> - * Change section sorting so that arm and ppc stub section is immediately after __text section + <rdar://problem/8032130> Linker complains about resolver functions when architecture is inferred. +2010-09-28 Nick Kledzik <kledzik@apple.com> -2009-06-17 Nick Kledzik <kledzik@apple.com> + <rdar://problem/6751424> ARM subtype not set on LTO programs - <rdar://problem/6975041> don't use no-PIC stubs in any dylibs - it might conflict with codesigning - * In StubAtom<arm>::StubAtom() don't use kStubNoPIC for OS dylibs - - ------ Tagged ld64-96.2 +2010-09-28 Nick Kledzik <kledzik@apple.com> -2009-06-09 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8481987> Link-Time Optimization assertion + Added test cases: + unit-tests/test-cases/lto-dead_strip-objc + unit-tests/test-cases/lto-dead_strip-some-hidden - * Back out page-cross branch work around +2010-09-24 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8441087> Support -dyld_env NAME=value ------ Tagged ld64-96.1 +2010-09-23 Nick Kledzik <kledzik@apple.com> -2009-06-05 Nick Kledzik <kledzik@apple.com> - - * Fix "duplicate symbol cache-line-crossing-stub" error by giving placeholders unique names - * Fix -pie by allowing relocs in x86 stubs and stub helpers - + Previous BranchIsland code changes to make buildable with clang++ were bad. Fix. ------ Tagged ld64-96 +2010-09-23 Nick Kledzik <kledzik@apple.com> -2009-06-04 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8274443> ld64 objc category merging asserts on category from old framework - * Darwin x86_64 static codegen is really dynamic code gen, so use LTO_CODEGEN_PIC_MODEL_DYNAMIC +2010-09-23 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8469063> ASLR Sidebuild: Many Projects Fail checksyms_read_only_relocs Verifier -2009-06-03 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6945923> use lto_codegen_set_assembler_path() +2010-09-22 Nick Kledzik <kledzik@apple.com> + Fix DOF section name bug -2009-06-03 Nick Kledzik <kledzik@apple.com> +2010-09-22 Nick Kledzik <kledzik@apple.com> - * In src/ld/LTOReader.hpp, move where -save-temps .bc file is saved to be after code model set + Fixes to build with clang++ +2010-09-21 Nick Kledzik <kledzik@apple.com> -2009-06-01 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6806033> Link Time Optimization error with 'dead code strip' + hidden symbol - * scan newAtoms returned from optimize() looking for ones already in the symbol table - * add test case unit-tests/test-cases/lto-dead_strip-all-hidden + In Resolver::fillInHelpersInInternalState(), dyld never needs stubs +2010-09-21 Ivan Krstic <ike@apple.com> -2009-05-19 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8457083> ld: support non-executable heap Mach-O header flag - <rdar://problem/6881656> make dyld stubs smaller/faster - * Add new addSynthesizedAtoms() method to Writer, called before atoms are sorted - * Fix throwf() and warning() to check printf types - * Add arm::kPointerDiff12 to support fast arm stubs - * Add new ContentType values for stubs and (non)lazy pointers - * Add new variant of arm stub this is one instruction +2010-09-21 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8175282> Xcode 4 linker fails with "address not in any section" -2009-05-13 Nick Kledzik <kledzik@apple.com> +2010-09-20 Nick Kledzik <kledzik@apple.com> - * ld64.xcodeproj/project.pbxproj: add warnings to dyldinfo target - * src/other/dyldinfo.cpp: support classic LINKEDIT format + <rdar://problem/8198832> assertion failure reading i386 yasm .o (not using scattered reloc) +2010-09-20 Nick Kledzik <kledzik@apple.com> -2009-05-12 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8430751> ld crashes when parsing dwarf and all code is not in __text - * src/ld/MachOWriterExecutable.hpp: fix optimization to skip branch islands with ARM bl instructions +2010-09-17 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8396450> magic segment symbol names don't work with preload executables + Update test case: unit-tests/test-cases/segment-labels -2009-05-12 Nick Kledzik <kledzik@apple.com> +2010-09-17 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6836647> creation of __unwind_info section can fail if hundreds of functions cannot be compact encoded - * src/ld/MachOWriterExecutable.hpp: fix when to make regular vs compressed pages + <rdar://problem/8445985> OSO in DebugNotes for LTO should point to generated mach-o not, bitcode .o file +2010-09-16 Nick Kledzik <kledzik@apple.com> -2009-05-08 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8413931> FUN in debug map not rebased + Update test case: unit-tests/test-cases/rebase-basic - * src/ld/ld.cpp: enhance -save-temps to also write out optimized bitcode file - +2010-09-16 Nick Kledzik <kledzik@apple.com> -2009-05-08 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8427133> Relocation failure with i386 32-bit diff to stub - * src/ld/ld.cpp: fix -order_file_statistics to print each symbol not found and correct total - +2010-09-16 Nick Kledzik <kledzik@apple.com> -2009-05-08 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8437287> assert when targeting 10.5 and crt1.o/dylib1.o is not supplied - <rdar://problem/6870522> linker should be able to coalesce UTF16 strings - * src/ld/MachOReaderRelocatable.hpp: parse __ustring section by labels but synthesize an - atom name based on the content. Leverage for __cfstring section - * unit-tests/test-cases/cfstring-utf16: update test case +-------- tagged ld64-118.1 +2010-09-15 Nick Kledzik <kledzik@apple.com> -2009-05-07 Nick Kledzik <kledzik@apple.com> + Fix missing rebase commands that broke perl - * src/ld/MachOWriterExecutable.hpp: put branch islands further apart if there is no thumb code +2010-09-15 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8252819> assert when .objc_class_name_* symbol missing + Add test case: unit-tests/test-cases/archive-ObjC-unexported -2009-05-07 Nick Kledzik <kledzik@apple.com> +2010-09-13 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6331300> LINKEDIT optimizations for iPhone - * src/ld/ObjectFile.h: Recognize iPhoneOS 3.1 - * src/ld/Options.cpp: iPhoneOS 3.1 => use compressed LINKEDIT - * src/ld/MachOWriterExecutable.hpp: support generating compressed LINKEDIT for arm + <rdar://problem/8419850> linker does not honor $ld$hide for umbrella libraries + Added test case: unit-tests/test-cases/symbol-hiding-umbrella +2010-09-09 Nick Kledzik <kledzik@apple.com> -2009-05-04 Nick Kledzik <kledzik@apple.com> + <rdar://problem/6723729> LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats - <rdar://problem/5716715> Add linker support for ARM branch islands - <rdar://problem/5253691> Add labels to linker synthesized jump islands - * src/ld/MachOWriterExecutable.hpp: reworked BranchIslandAtom and createBranchIslands to support arm/thumb - * src/ld/ObjectFile.h: added kBranchIsland - * unit-tests/test-cases/branch-islands: updated test case to check arm/thumb +2010-09-09 Nick Kledzik <kledzik@apple.com> + <rdar://problem/4942948> support -bind_at_load -2009-04-30 Nick Kledzik <kledzik@apple.com> +2010-09-07 Nick Kledzik <kledzik@apple.com> - * src/ld/Options.cpp: fix custom stack base address for arm + <rdar://problem/7989734> ld mis-handling std::tr1::anonymous symbols + Remove support for ordering gcc-4.0 compiled anonymous namespace symbols -2009-04-30 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-118 - <rdar://problem/6818272> likely incorrect warning about common symbols - * src/ld/Options.cpp: ignore LD_WARN_COMMONS in -r mode - +2010-09-02 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-95.8.3 + <rdar://problem/8308697> -preload should not have LINKEDIT segment + Added test case: unit-tests/test-cases/efi-basic -2009-03-31 Nick Kledzik <kledzik@apple.com> +2010-09-02 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6719270> ld might set MH_WEAK_DEFINES when it should not - * src/ld/MachOWriterExecutable.hpp: rescan fRegularDefAtomsThatOverrideADylibsWeakDef and only consider global ones - + <rdar://problem/8389578> trivial Objective-C app fails when using libLTO + Added test case: unit-tests/test-cases/lto-objc-image-info ------ Tagged ld64-95.8.2 +2010-09-02 Nick Kledzik <kledzik@apple.com> -2009-03-18 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8384727> Add -reexport_symbols_list option to re-export certain symbols. + Added test case: unit-tests/test-cases/reexport_symbols_list - * src/ld/MachOReaderRelocatable.hpp: back out -force_cpusubtype_ALL changes +2010-09-01 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7438246> LTO with 'dead code strip' can't ignore unused functions with undefined references + Add test case: unit-tests/test-cases/lto-dead_strip-unused ------ Tagged ld64-95.8.1 +2010-09-01 Nick Kledzik <kledzik@apple.com> -2009-03-17 Nick Kledzik <kledzik@apple.com> + Warn if unaligned ARM code is detected - <rdar://problem/6578416> -dead_strip inhibits weak coalescing in no_dead_strip section - * src/ld/ld.cpp: in markDead() remove from fLiveRootAtoms - +2010-09-01 Nick Kledzik <kledzik@apple.com> -2009-03-17 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8383175> Mach-O linked by current linker don't load in VIrtualBox any more - <rdar://problem/6671072> libgcc fails to build in with ld64-95.8 - * src/ld/MachOReaderRelocatable.hpp: interpret CPU_SUBTYPE_ARM_ALL as CPU_SUBTYPE_ARM_V4T - * src/ld/Options.cpp: interept -force_cpusubtype_ALL as -arch armv4t +2010-09-01 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8304984> Linker should pick default visibility instead of warning about conflicts + Updated test case: unit-tests/test-cases/visibility-warning ------ Tagged ld64-95.8 +2010-09-01 Nick Kledzik <kledzik@apple.com> -2009-02-09 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8249338> Enable new load commands - <rdar://problem/6569316> ld64-95.7 crashes building Foundation-678.39 in side build - * src/ld/MachOReaderRelocatable.hpp: handle a zero length section with a label before __cstring section +2010-09-01 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8368679> Do not pass -demangle to ld_classic ------ Tagged ld64-95.7 +2010-09-01 Nick Kledzik <kledzik@apple.com> -2009-02-06 Nick Kledzik <kledzik@apple.com> - - * src/ld/ObjectFile.h: make fAddCompactUnwindEncoding false by default - + <rdar://problem/8336910> iOS 4.3 armv7 should be PIE by default + <rdar://problem/5472933> better error message for direct access to external globals when not linking read_only_relocs + <rdar://problem/7927510> linker does not error on direct (static) data references to a dylib symbol -2009-02-06 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-117.11 - <rdar://problem/6537210> ER: add linker option to zero fill empty DATA sections on disk - * src/ld/Options.cpp: add support for -no_zero_fill_sections - * src/ld/MachOReaderRelocatable.hpp: isZeroFill() is only true if fOptimizeZeroFill - * doc/man/man1/ld.1: document -no_zero_fill_sections - * unit-tests/test-cases/no_zero_fill_sections: add test case - +2010-09-03 Nick Kledzik <kledzik@apple.com> -2009-02-05 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8385011> mask thumb bit off non lazy pointers content when parsing arm .o files - <rdar://problem/6497366> label getting resolved to the wrong address. - * src/ld/MachOWriterExecutable.hpp: add findAtomAndOffsetForSection() and use it to disambiguate - * unit-tests/test-cases/label-on-end-of-section: added test case +-------- tagged ld64-117.10 +2010-08-26 Nick Kledzik <kledzik@apple.com> -2009-01-27 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8358084> 8F64 kills installtest devices + Don't clear thumb bit on pointers inside thumb functions if addend is negative - <rdar://problem/6517393> Warn -force_cpusubtype_ALL is not supported - * src/ld/Options.cpp: warn if fForceSubtypeAll and fArchitecture is CPU_TYPE_ARM - +-------- tagged ld64-117.9 ------ Tagged ld64-95.6 +2010-08-25 Nick Kledzik <kledzik@apple.com> -2009-01-25 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8345038> no more audio because of broken thunk + Update of thumb22 b.w instruction was not clearing bits before or'ing in new ones - <rdar://problem/6439020> Add support for section start/end labels - * src/ld/ObjectFile.h: add kSectionStart and kSectionEnd - * src/ld/MachOReaderRelocatable.hpp: create SectionBoundaryAtoms as needed - * src/ld/ld.cpp: sort SectionBoundaryAtoms correctly - * src/ld/MachOWriterExecutable.hpp: allow all relocations in preload images +-------- tagged ld64-117.8 +2010-08-25 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-95.5 + <rdar://problem/8342028> prefetch abort in kernel mode: fault_addr=0xe58024e4 + Don't set thumb bit on .long pointers in thumb functions that point to some offset in same function -2009-01-15 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-117.7 - * src/ld/MachOWriterExecutable.hpp: in hasPageCrossingBranches() ignore branches preceeded by a branch +2010-08-24 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8346444> dyld-179.4 fails to link, assert in setLoadCommandsPadding() + Fix linker to always put __text first before other code-only sections ------ Tagged ld64-95.4 +-------- tagged ld64-117.6 -2009-01-15 Nick Kledzik <kledzik@apple.com> +2010-08-23 Nick Kledzik <kledzik@apple.com> - * src/ld/Options.cpp: handle -kext and -r the same for fPreventPageCrossingBranches + <rdar://problem/8339702> ld no longer output static archive dependencies for dyld for B&I + Add test case unit-tests/test-cases/dependency-logging +-------- tagged ld64-117.5 ------ Tagged ld64-95.3 +2010-08-20 Nick Kledzik <kledzik@apple.com> -2009-01-14 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8309595> SWB: ld64-117.1 on Durango8F54: Assertion failed: + UTF16 CFStrings were not coalesced correctly when gcc built the .o files and the + last string in the __ustring section only had a single zero byte at the end. + +-------- tagged ld64-117.4 - <rdar://problem/6497574> linker should alter layout to prevent armv7 page crossing branches - * src/ld/Options.cpp: set fPreventPageCrossingBranches - * src/ld/MachOWriterExecutable.hpp: adjust layout of __text so there are not page crossing branches - * src/ld/MachOReaderRelocatable.hpp: support new ARM_THUMB_32BIT_BRANCH reloce +2010-08-19 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8326544> DTSJ1J105: SpringBoard crashes on boot + Fix order file to move aliases even when subsections_via_symbols it used + Update test case unit-tests/test-cases/order_file ------ Tagged ld64-95.2.10 +2010-08-17 Nick Kledzik <kledzik@apple.com> -2009-04-02 Nick Kledzik <kledzik@apple.com> + Fix resolver functions to survive ld -r. + Warn if resolver functions are made non-external. - <rdar://problem/6805002> corrupt metaclass entry in dynamic library - * src/ld/ld.cpp: change Section constructor to copy segment and section names +2010-08-17 Nick Kledzik <kledzik@apple.com> + Make it an error for resolver functions to be used in anything but a dylib. ------ Tagged ld64-95.2.9 +-------- tagged ld64-117.3 -2009-04-02 Nick Kledzik <kledzik@apple.com> +2010-08-17 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6744267> Update ld64 for new triples introduced in 6654669 to support ARM LLVM - * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples + Fix thumb resolver functions + Enable updward dylibs and symbol re-exports for iOS 4.2 +2010-08-16 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-95.2.8 + <rdar://problem/8308697> Latest ld no longer supports EFI -preload + Rearrange LINKEDIT chunks in -preload mode -2009-03-24 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-117.2 - <rdar://problem/6713931> anonymous functions have the compact unwind info computed wrong - * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom +2010-08-14 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8308697> Latest ld no longer supports EFI -preload + Add LC_UNIXTHREAD to -preload ------ Tagged ld64-95.2.7 +2010-08-14 Nick Kledzik <kledzik@apple.com> -2009-03-11 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8309530> SWB: ld64-117.1 on Durango8F54: Assertion failed: (categoryAtom->size() == Category<A>::size()) + gcc-4.0 uses 'L' labels on categories. This merges them onto previous data and disable category optimzation - <rdar://problem/6670421> AddressBook incorrectly gets _objc_msgSend from WebKit - * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib - that is already explictly or implicitly linked. - * unit-tests/test-cases/re-export-optimizations-indirect: add test case - +2010-08-14 Nick Kledzik <kledzik@apple.com> -2009-03-10 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8309917> SWB: ld64-117.1 on Durango8F54: bad category optimization + Disable category optimization for i386 and arm until further testing - <rdar://problem/6665853> dyld weak linking optimization leaves some symbols unbound - * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference - to a symbol in a dylib that is a weak definition - * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case +2010-08-14 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8309608> SWB: ld64-117.1 on Durango8F54: address not in any section + Handle pointer diff to stub for weak hidden function -2009-03-10 Nick Kledzik <kledzik@apple.com> +2010-08-13 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6666004> many OS i386 OS dylibs still have __IMPORT segment - * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr - * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem + <rdar://problem/8308697> Latest ld no longer supports EFI -preload +-------- tagged ld64-117.1 ------ Tagged ld64-95.2.6 +2010-08-11 Nick Kledzik <kledzik@apple.com> -2009-02-27 Nick Kledzik <kledzik@apple.com> + Make missing exported symbols a warning to help adoption of new linker - <rdar://problem/6633530> ld might set MH_WEAK_DEFINES when it should not - * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef - that will be exported when computing MH_WEAK_DEFINES - * unit-tests/test-cases/operator-new: updated to reproduce issue - +2010-08-11 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-95.2.5 + Add ExternalRelocationsAtom<>::pointerReloc() to more easily support kext bundles -2009-02-24 Nick Kledzik <kledzik@apple.com> +2010-08-09 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped - * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings - * unit-tests/test-cases/objc-literal-pointers-strip: added test case - + <rdar://problem/8210380> SWB: ld64-116.2 fix branch to label-4 ------ Tagged ld64-95.2.4 +2010-08-09 Nick Kledzik <kledzik@apple.com> -2009-02-23 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8260073> Error with empty non_lazy_symbol_pointers section - * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs +2010-08-06 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7977374> Add command line options to control symbol weak-def-bit on exported symbols -2009-02-18 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-117 - <rdar://problem/6583555> Writer<A>::symbolIndex() uses a linear search and does not scale - * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better - +2010-07-28 Nick Kledzik <kledzik@apple.com> -2009-02-18 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8243431> split seg info wrong for x86_64 stub helpers - <rdar://problem/6312070> Use new compact encodings that handle all register permutations - * src/ld/Architectures.hpp: add kSectionOffset24 - * src/ld/ObjectFile.h: add getFDE() - * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding - * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed - * src/other/unwinddump.cpp: update unwinddump output to display register save set - +2010-07-26 Nick Kledzik <kledzik@apple.com> -2009-02-16 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8237436> __nlcatlist categories should not be optimized - <rdar://problem/6511619> runtime error with bundle for 10.5 that has weak external symols - * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions - +2010-07-23 Nick Kledzik <kledzik@apple.com> -2009-02-15 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8179273> ld64 assertion on object file - <rdar://problem/6583757> i386 relocation error with negative offsets from local labels - * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label - * unit-tests/test-cases/relocs-neg-from-local: add test case - +2010-07-21 Nick Kledzik <kledzik@apple.com> -2009-02-12 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7435296> Reorder sections to reduce page faults in object files - <rdar://problem/6578360> -dead_strip inhibits weak coalescing in no_dead_strip section - * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms - * unit-tests/test-cases/dead_strip-weak-coalesce: added test case +2010-06-30 Nick Kledzik <kledzik@apple.com> + Support resolver functions in iOS dylibs -2009-02-12 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-116.2 - <rdar://problem/6581809> x86_64 weak_import broken for initialized data - * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader<x86_64>::addRelocReference() - * src/other/dyldinfo.cpp: update to display weak_import attribute - * unit-tests/test-cases/weak_import: updated test case +2010-06-30 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8138287> C programs get objc GCness from dylibs + Update: unit-tests/test-cases/objc-gc-checks -2009-02-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6541812> ld parsing of __eh_frame unwind information is slow - * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 - +-------- tagged ld64-116.1 ------ Tagged ld64-95.2.3 +2010-06-22 Nick Kledzik <kledzik@apple.com> -2009-02-04 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8111013> address range check should not apply to preload executables - <rdar://problem/6545406> ld: warning: can't add line info to anonymous symbol - * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs +2010-06-22 Nick Kledzik <kledzik@apple.com> + Warn instead of error when CPU_SUBTYPE_ARM_ALL .o files used. ------ Tagged ld64-95.2.2 +2010-06-22 Nick Kledzik <kledzik@apple.com> -2009-02-02 Nick Kledzik <kledzik@apple.com> + Fix assert in objc category optimzation. Metaclass also has copy of propery list to update. - <rdar://problem/6548268> ld -r does not preserve the N_NO_DEAD_STRIP bit - * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() - * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case +2010-06-22 Nick Kledzik <kledzik@apple.com> + Fix crash in -r mode with -alias. ------ Tagged ld64-95.2.1 +-------- tagged ld64-116 ------ Tagged ld64-95.2.10 +2010-06-21 Nick Kledzik <kledzik@apple.com> -2009-04-02 Nick Kledzik <kledzik@apple.com> + Add support for -ios_version_min as an alias for -iphoneos_version_min - <rdar://problem/6805002> corrupt metaclass entry in dynamic library - * src/ld/ld.cpp: change Section constructor to copy segment and section names +2010-06-21 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7687304> linker could merge method lists from class and its categories + Added test case: unit-tests/test-cases/objc-category-optimize + Added option: -no_objc_category_merging to disable ------ Tagged ld64-95.2.9 +2010-06-21 Nick Kledzik <kledzik@apple.com> -2009-04-02 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8113877> i386 TLV PIC reloc content is negated - <rdar://problem/6744267> Update ld64 for new triples introduced in 6654669 to support ARM LLVM - * src/ld/LTOReader.hpp: change "arm-" to "arm" so matching works for new triples +2010-06-15 Nick Kledzik <kledzik@apple.com> + Added better error messages and asserts for bad thread local object files ------ Tagged ld64-95.2.8 +2010-06-09 Nick Kledzik <kledzik@apple.com> -2009-03-24 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8076986> Barolo: 'rebase' makes timestamps invalid/unreadable for GDB - <rdar://problem/6713931> anonymous functions have the compact unwind info computed wrong - * ld/MachOReaderRelocatable.hpp: use new compact unwind function in AnonymousAtom +2010-06-09 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7966333> executable has no debug symbols when compiled with LTO + Added test case: unit-tests/test-cases/lto-object_path ------ Tagged ld64-95.2.7 +2010-06-09 Nick Kledzik <kledzik@apple.com> -2009-03-11 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7702923> stop promoting hidden referenced dynamically symbols to global + Updated test case: unit-tests/test-cases/main-stripped - <rdar://problem/6670421> AddressBook incorrectly gets _objc_msgSend from WebKit - * src/ld/MachOReaderDylib.hpp: fix processIndirectLibraries() to not force a private re-export of a dylib - that is already explictly or implicitly linked. - * unit-tests/test-cases/re-export-optimizations-indirect: add test case - - -2009-03-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6665853> dyld weak linking optimization leaves some symbols unbound - * src/ld/MachOWriterExecutable.hpp: be sure to create bind entry for a reference - to a symbol in a dylib that is a weak definition - * unit-tests/test-cases/coalesce_weak_def_in_dylib: add test case - - -2009-03-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6666004> many OS i386 OS dylibs still have __IMPORT segment - * ld/MachOReaderRelocatable.hpp: moved where __IMPORT/__pointer is changed to __DATA/__nl_symbol_ptr - * unit-tests/test-cases/stripped-indirect-symbol-table: updated to test for this problem - - ------ Tagged ld64-95.2.6 +2010-06-04 Nick Kledzik <kledzik@apple.com> -2009-02-27 Nick Kledzik <kledzik@apple.com> + <rdar://problem/6144674> ER: individual symbol re-exports + Added test case: unit-tests/test-cases/re-export-symbo - <rdar://problem/6633530> ld might set MH_WEAK_DEFINES when it should not - * src/ld/MachOWriterExecutable.hpp: only consider atoms in fRegularDefAtomsThatOverrideADylibsWeakDef - that will be exported when computing MH_WEAK_DEFINES - * unit-tests/test-cases/operator-new: updated to reproduce issue - - ------ Tagged ld64-95.2.5 - -2009-02-24 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped - * src/ld/MachOWriterExecutable.hpp: in setLocalNlist() don't use 'l' labels for x86_64 strings - * unit-tests/test-cases/objc-literal-pointers-strip: added test case - +2010-06-03 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-95.2.4 + <rdar://problem/7474224> add functions start info to LINKEDIT + * Support added but not on by default. Use -function_starts to enable. + * Added test case: unit-tests/test-cases/function-start -2009-02-23 Nick Kledzik <kledzik@apple.com> +2010-06-02 Nick Kledzik <kledzik@apple.com> - * src/ld/MachOReaderRelocatable.hpp: ignore ARM_THUMB_32BIT_BRANCH relocs + <rdar://problem/5674241> ER: add load command for min OS version + * Support added but not on by default. Use -version_load_command to enable. +2010-06-02 Nick Kledzik <kledzik@apple.com> -2009-02-18 Nick Kledzik <kledzik@apple.com> + <rdar://problem/8040089> provide better undefined symbol error message - <rdar://problem/6583555> Writer<A>::symbolIndex() uses a linear search and does not scale - * src/ld/MachOWriterExecutable.hpp: build a std::map so symbolIndex() scales better - +2010-05-28 Nick Kledzik <kledzik@apple.com> -2009-02-18 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7780438> ld should also merge file attributes from lazy loaded archives + * Move attribute gathering from InputFiles to Resolver - <rdar://problem/6312070> Use new compact encodings that handle all register permutations - * src/ld/Architectures.hpp: add kSectionOffset24 - * src/ld/ObjectFile.h: add getFDE() - * src/ld/MachOReaderRelocatable.hpp: use new libunwind functions to get new compact encoding - * src/ld/MachOWriterExecutable.hpp: use new compact encoding which includes offset in dwarf if needed - * src/other/unwinddump.cpp: update unwinddump output to display register save set - +2010-05-28 Nick Kledzik <kledzik@apple.com> -2009-02-16 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/8038333> SWB: ld64-115.3: dylib on kext link line causes malformed kext + * allow -static after -kext on command line - <rdar://problem/6511619> runtime error with bundle for 10.5 that has weak external symols - * src/ld/ld.cpp: fix hybrid (10.5) compressed linkedit info for data pointing to weak definitions - +-------- tagged ld64-115.3 -2009-02-15 Nick Kledzik <kledzik@apple.com> +2010-05-26 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6583757> i386 relocation error with negative offsets from local labels - * src/ld/MachOReaderRelocatable.hpp: handle when base addr of scattered relocation does not point to a label - * unit-tests/test-cases/relocs-neg-from-local: add test case - + * <rdar://problem/8024702> strip of .o files removes __objc_imageinfo section + * Added test case: unit-tests/test-cases/dwarf-strip-objc -2009-02-12 Nick Kledzik <kledzik@apple.com> +2010-05-25 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6578360> -dead_strip inhibits weak coalescing in no_dead_strip section - * src/ld/ld.cpp: remove atoms coalesced away from fLiveRootAtoms - * unit-tests/test-cases/dead_strip-weak-coalesce: added test case + * <rdar://problem/8023624> crash when parsing local vanilla reloc to weak def +-------- tagged ld64-115.2 -2009-02-12 Nick Kledzik <kledzik@apple.com> +2010-05-21 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6581809> x86_64 weak_import broken for initialized data - * src/ld/MachOReaderRelocatable.hpp: use isWeakImportSymbol() in Reader<x86_64>::addRelocReference() - * src/other/dyldinfo.cpp: update to display weak_import attribute - * unit-tests/test-cases/weak_import: updated test case + * <rdar://problem/8012536> switch back to using ld_classic for -static arm code -2009-02-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6541812> ld parsing of __eh_frame unwind information is slow - * src/ld/MachOReaderRelocatable.hpp: build a std::map of all __eh_frame relocations for x86_64 - - ------ Tagged ld64-95.2.3 - -2009-02-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6545406> ld: warning: can't add line info to anonymous symbol - * src/ld/MachOReaderRelocatable.hpp: don't warn about line info in dyld stubs - - ------ Tagged ld64-95.2.2 +2010-05-21 Nick Kledzik <kledzik@apple.com> -2009-02-02 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/8012549> warn instead of error when seg1addr is out of range for ARM - <rdar://problem/6548268> ld -r does not preserve the N_NO_DEAD_STRIP bit - * src/ld/MachOWriterExecutable.hpp: set N_NO_DEAD_STRIP based on dontDeadStrip() - * unit-tests/test-cases/dead_strip-r_symbol_desc: added test case +2010-05-21 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-95.2.1 + * <rdar://problem/8012526> fix -undefined dynamic_lookup -nodefaults to not error about missing dyld_stub_binder -2009-01-29 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-115.1 - <rdar://problem/6532377> gcc DejaGnu failure: building longcall/dylib library - * src/ld/MachOWriterExecutable.hpp: if no __DATA sections insert non-lazy pointers at end of __TEXT segment - * unit-tests/test-cases/no-data-bundle: added test case +2010-05-19 Nick Kledzik <kledzik@apple.com> + * Fix trie nodes for resolver functions to have second address be stub not helper ------ Tagged ld64-95.2 - -2009-01-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6476760> strip -S fails with "new trie is larger than original" - * src/other/PruneTrie.cpp: don't align trie more than original trie was aligned - - ------ Tagged ld64-95.1 - -2008-12-21 Nick Kledzik <kledzik@apple.com> - - * src/ld/MachOWriterExecutable.hpp: in new linkedit format, make sure only exported symbols - make it into weak binding info - - ------ Tagged ld64-95 +2010-05-19 Nick Kledzik <kledzik@apple.com> -2008-12-18 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7996423> work around for old checksyms tools + * Make i386 stub section named "__symbol_stub" instead of "__stubs" - * src/ld/Options.cpp: move check for fSharedRegionEligible until fPrebind has stabilized - +2010-05-10 Nick Kledzik <kledzik@apple.com> -2008-12-18 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7960396> linking with LTO prints "/tmp/lto.o" - <rdar://problem/6305021> Generate new compressed LINKEDIT when targeting 10.6 - * src/ld/Options.cpp: turn on compressed LINKEDIT by default - +-------- tagged ld64-115 ------ Tagged ld64-94.1 +2010-05-06 Nick Kledzik <kledzik@apple.com> -2008-12-16 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7892392> linker loses x86_64 addend to 'L' symbols + * properly handle addend to 'L' symbols that are ignored - * src/ld/Options.cpp: Fix -F handling in buildSearchPaths() +2010-05-05 Nick Kledzik <kledzik@apple.com> + * rework min OS version parsing to enable the linker to handle unknown OS versions ------ Tagged ld64-94 -2008-12-15 Nick Kledzik <kledzik@apple.com> +2010-05-05 Nick Kledzik <kledzik@apple.com> - * doc/man/man1/ld.1: document new options + * Implement magic section$start$xxx$yyyy and section$end$xxx$yyyy symbols + * Implement magic segment$start$xxx and segment$end$xxx symbols + * Add test case: unit-tests/test-cases/segment-labels -2008-12-15 Nick Kledzik <kledzik@apple.com> +2010-05-03 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6134468> linker should enforce all .o files have same sub-type, and ignore sub-type of dylibs - * doc/man/man1/ld.1: update man page about -allow_sub_type_mismatches - * src/ld/ld.cpp: call validFile() with new arguments - * src/ld/MachOReaderRelocatable.hpp: add new arguments to validFile() - * src/ld/Options.cpp: Support LD_ALLOW_CPU_SUBTYPE_MISMATCHES and -allow_sub_type_mismatches + * <rdar://problem/7173071> implement optional demangling in linker + * Add option: -demangle + * Add test case: unit-tests/test-cases/demangle -2008-12-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6438270> -syslibroot should skip standard search paths not in the SDK - * src/ld/Options.cpp: in buildSearchPaths() if an SDK is specified don't add - standard search paths not in the SDK. - - -2008-12-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6406609> ld: remove "can't make compact unwind encoding" warning - * src/ld/ObjectFile.h: add fWarnCompactUnwind - * src/ld/Options.cpp: -warn_compact_unwind --> fWarnCompactUnwind - * src/ld/MachOReaderRelocatable.hpp: test fWarnCompactUnwind before warning - - -2008-12-15 Nick Kledzik <kledzik@apple.com> +2010-05-03 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6442926> Add dtrace usdt support for arm to ld64 - * src/ld/MachOWriterExecutable.hpp: handle arm::kDtraceIsEnabledSite - * unit-tests/test-cases/dtrace-static-probes: use is-enabled in test case + * <rdar://problem/7931759> ld64 doesn't grok the modern-objc-ABI-on-i386 + * Add support for -objc_abi_version command line option + * Added test case: unit-tests/test-cases/objc-abi ------ Tagged ld64-93 +2010-05-03 Nick Kledzik <kledzik@apple.com> -2008-12-11 Nick Kledzik <kledzik@apple.com> - - * src/ld/ObjectFile.h: add fIPhoneVersionMin to track min iPhoneOS version - * src/ld/Options.cpp: use fIPhoneVersionMin + * <rdar://problem/7929974> -alias does not work with __OBJC sections + * sort contents of sections with aliases + * Added test case: unit-tests/test-cases/objc-class-alias -2008-12-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6431277> non-lazy pointer to non-global tentative definition encoded wrong - * src/ld/MachOWriterExecutable.hpp: don't use INDIRECT_SYMBOL_LOCAL for tentative definitions - * unit-tests/test-cases/non-lazy-r: updated test case - - -2008-12-11 Nick Kledzik <kledzik@apple.com> +2010-04-28 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6437667> kernel fails to boot when ld64 used for intermediate ld -r step - * src/ld/MachOWriterExecutable.hpp: in -r mode when generating a scattered sect-diff reloc for - i386/arm, special case when from target is not the atom - the relocation is in. - * unit-tests/test-cases/relocs-asm: update test case + * <rdar://problem/4966836> Feature: Thread local storage + * Add test case: unit-tests/test-cases/tlv-basic + * Add test case: unit-tests/test-cases/tlv-dylib -2008-12-11 Nick Kledzik <kledzik@apple.com> - - * src/ld/ld.cpp: handle new __program_vars section - * src/ld/MachOWriterExecutable.hpp: handle inserting synthesized sections when there is no __dyld section - - -2008-12-11 Nick Kledzik <kledzik@apple.com> - - * src/ld/MachOReaderRelocatable.hpp: Fix getDescription() to work when direct reference is to anonymous atom +2010-04-27 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/4383612> Accelerate needs way to dispatch based on instruction execution time characteristics. + * Add support for "symbol resolver" functions + * Add test case unit-tests/test-cases/symbol-resolver-basic -2008-12-10 Nick Kledzik <kledzik@apple.com> - * src/ld/Options.cpp: enable LD_FORCE_NO_PREBIND to be used with arm +2010-04-26 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7711820> range check fat archives + * check that fat file slice being used does not extend beyond end of file + * check that member being used does not extend beyond end of slice/file -2008-12-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6258169> Developer tool to print the new compressed LINKEDIT information - * src/other/dyldinfo.cpp: fix typo in usage() - - -2008-12-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6308882> SnowLeopard kernel should compile warning free - * src/ld/MachOReaderRelocatable.hpp: correct parse two global labels at end of section and make one an alias - * unit-tests/test-cases/end-label: update test case +2010-04-26 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7666015> The documentation for the -allowable_client option doesn't say enough about it -2008-12-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6342245> Better warning than "PPC_RELOC_JBSR should not be using an external relocation" - * src/ld/MachOReaderRelocatable.hpp: issue warning with .o path if it was compiled with -mlong-branch - - -2008-12-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6408832> linker should not map __pointers -> __nl_symbol_ptr unless actually making new LINKEDIT - * src/ld/ObjectFile.h: add fMakeCompressedDyldInfo for readers to see - * src/ld/Options.cpp: set fMakeCompressedDyldInfo for readers to see - * src/ld/MachOReaderRelocatable.hpp: check fMakeCompressedDyldInfo +2010-04-26 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7854068> back out LD_NO_PIE -2008-12-02 Nick Kledzik <kledzik@apple.com> +2010-04-22 Nick Kledzik <kledzik@apple.com> - * src/ld/debugline.c: fix error handling in line_open() + * <rdar://problem/7831043> More ICU make check failures with 0-terminated UTF16 strings + * Change UTF16StringSection to break into atoms just on label boundaries + * Added test case: unit-tests/test-cases/utf16-nul -2008-11-26 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-114.12 - <rdar://problem/6401277> vtable with thumb entries broke after ld -r - * src/ld/MachOReaderRelocatable.hpp: if target of reloc is thumb, mask thumb bit off addend - * unit-tests/test-cases/thumb-pointer: added test case +2010-04-14 Nick Kledzik <kledzik@apple.com> - -2008-11-26 Nick Kledzik <kledzik@apple.com> - - * src/ld/Option.cpp: Fix how crashreporterBuffer is created to not miss some arguments + * <rdar://problem/7811357> Crash with messed up BNSYM -2008-11-24 Nick Kledzik <kledzik@apple.com> +2010-04-07 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6398605> Security.framework has some duplicate FDEs for some functions - * src/ld/ld.cpp: remove fDeadAtoms from fLiveAtoms when there are weak atoms overriden by late loads - * unit-tests/test-cases/dead_strip-archive-eh: added test case + * Fix crash with blank dylib stubs +-------- tagged ld64-114.11 ------ Tagged ld64-92 +2010-04-07 Nick Kledzik <kledzik@apple.com> -2008-11-21 Nick Kledzik <kledzik@apple.com> - - * src/ld/MachOReaderDylib.hpp: if export_size is zero, no need to parse trie - * src/abstraction/MachOTrie.hpp: gracefully handle empty trie + * <rdar://problem/7798495> for ppc, add split-seg info for TEXT pointers to DATA -2008-11-21 Nick Kledzik <kledzik@apple.com> +2010-04-07 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6257854> strip(1) support for new compressed LINKEDIT information - * ld64.xcodeproj/project.pbxproj: build and install new libprunetrie.a - * src/other/prune_trie.h: added - * src/other/PruneTrie.cpp: implements prune_trie() - + * <rdar://problem/7831379> Cannot build ppc64 target with ObjC code -2008-11-21 Nick Kledzik <kledzik@apple.com> - * src/ld/ld.cpp: if an export file is used and all weak symbols are masked, don't set WEAK_DEFINES - * unit-tests/test-cases/weak-def-flag: added test case +2010-04-01 Nick Kledzik <kledzik@apple.com> + * let .exp files override auto-hide so that they can be exported if needed -2008-11-20 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6305021> Generate new compressed LINKEDIT when targeting 10.6 - * src/ld/MachOWriterExecutable.hpp: support generating new compressed format - * src/ld/MachOReaderRelocatable.hpp: new compress format implies non-lazy pointers in __DATA for i386 - * src/ld/MachOReaderDylib.hpp: support linking aginst new format - * src/ld/Options.cpp: suppport -exported_symbols_order and -no_compact_linkedit - * src/ld/ld.cpp: track which atoms have weak counter parts in dylibs - * src/other/dyldinfo.cpp: added tool to display new LINKEDIT format - * ld64.xcodeproj/project.pbxproj: add dyldinfo tool - * unit-tests/*: lots of fixes to work with new format +2010-04-01 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden + * added test case: unit-tests/test-cases/weak-def-auto-hide -2008-11-20 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6389316> ld64 should preserve N_WEAK_REF when linking MH_KEXT_BUNDLEs - * src/ld/MachOWriterExecutable.hpp: set up fWeakImportMap in synthesizeKextGOT() +2010-04-01 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7818475> 'l' symbols not being automatically removed -2008-11-19 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6342406> VideoToolbox.framework has bad __TEXT.__eh_frame info - * src/ld/Options.cpp: add -no_eh_labels option for use with -r - * src/ld/MachOWriterExecutable.hpp: generate correct x86_64 labeless relocs in -r mode - * src/ld/MachOReaderRelocatable.hpp: now ignore all labels and relocations in - __TEXT/__eh_frame section and rely on getCFIs() from libunwind - * unit-tests/test-cases/eh-coalescing-no-labels: add test case +2010-03-31 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7808258> weak defs should not cause indirection in static executables + * Update test case: unit-tests/test-cases/static-executable-weak-defines -2008-11-19 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-114.10 - <rdar://problem/6378110> LTO doesn't like dtrace symbols - * src/ld/LTOReader.hpp: ignore __dtrace_probe undefines in bitcode files +2010-03-31 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7735120> assert with .o file with two LSDA sections -2008-11-14 Nick Kledzik <kledzik@apple.com> - * src/abstraction/MachOFileAbstraction.hpp: fix to work with 10.5 headers - - ------ Tagged ld64-91 +-------- tagged ld64-114.9 -2008-11-07 Nick Kledzik <kledzik@apple.com> - - Remove COMPACT_UNWIND_SUPPORT conditionalizing +2010-03-30 Nick Kledzik <kledzik@apple.com> - -2008-11-06 Nick Kledzik <kledzik@apple.com> - - Reorganize source layout. ld sources are now in "ld", - and other tools are in "other". + * <rdar://problem/7791161> L4 locks up starting a second processor, works fine with old linker + * properly get addend from content in x86_64 substractor when target is direct -2008-11-05 Nick Kledzik <kledzik@apple.com> - - * ld64.xcodeproj/project.pbxproj: start installing unwinddump tool - * src/UnwindDump.cpp: support -arch option - * doc/man/man1/unwinddump.1: create man page - - -2008-11-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6337329> linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries - * src/ld.cpp: in synthesizeDebugNotes() set other field of OSO to be subtype - - -2008-11-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/3738966> Need a linker option to load all objects from one library - * src/Options.cpp: support -force_load option - * src/ArchiveReader.hpp: Add fForceLoad ivar - * doc/man/man1/ld.1: update man page with -force_load option - * unit-tests/test-cases/archive-force-load: add test case +2010-03-29 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7805172> ld should excludes debug notes when computing UUID + * added test case: unit-tests/test-cases/dwarf-debug-notes-uuid -2008-11-05 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-114.8 - <rdar://problem/6308882> Dtrace Probe Warnings: SnowLeopard kernel should compile warning free - * src/ld.cpp: don't generate GSYM stabs for old style __dtrace_probe - * src/MachOReaderRelocatable.hpp: fix test for deciding if a symbol is an alias - - -2008-11-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6294378> ADOBE: XCODE: ld: duplicate typeinfo in executable - * src/ld.cpp: in dead-strip mode, record overriden symbols and later rebind all uses - * unit-tests/test-cases/dead_strip-archive-weak: add test case - +2010-03-26 Nick Kledzik <kledzik@apple.com> -2008-11-03 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7788474> __objc_catlist section loses don't dead strip bit in ld -r mode + * Update test case unit-tests/test-cases/static-executable - <rdar://problem/6254202> support increased branch range in Thumb-2 - * src/MachOReaderRelocatable.hpp: handle full branch range in addRelocReference() - * unit-tests/test-cases/branch-distance: added test case +-------- tagged ld64-114.7 -2008-10-31 Devang Patel <dpatel@apple.com> - - <rdar://problem/5725712> Sqlite 3.5.4 built with lvm-gcc-4.2 -O4 fails regression test - * src/LTOReader.hpp: Use real atom scope when real atom is available. - Preserve globals while optimizing an executable. - -2008-10-30 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: support all encodings in getEncodedP() - +2010-03-25 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-90 + * <rdar://problem/7796313> Support LD_NO_PIE again -2008-10-30 Nick Kledzik <kledzik@apple.com> +2010-03-25 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6327584> icc has dwarf unwind info that is different than gcc - * src/MachOReaderRelocatable.hpp: support more encodings in getEncodedP() + * <rdar://problem/7765885> Rosetta crashes on launch in 11A133a + * Fix -segaddr __TEXT to cause other floating segments to be contiguous with TEXT +2010-03-24 Nick Kledzik <kledzik@apple.com> -2008-10-23 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7786326> Page Zero segment seems to be getting dead stripped - <rdar://problem/6088653> build ld64 for x86_64 - * ld64.xcodeproj/project.pbxproj: add X86_64 to valid archs - - -2008-10-23 Nick Kledzik <kledzik@apple.com> +2010-03-24 Nick Kledzik <kledzik@apple.com> - * ld64.xcodeproj/project.pbxproj: use generated @$(DERIVED_FILE_DIR)/linker_opts for extra - linker options. This allows linker to be built if LTO headers and libs are missing. + * <rdar://problem/7785574> kernel sdt dtrace probes not visible +-------- tagged ld64-114.6 -2008-10-23 Nick Kledzik <kledzik@apple.com> +2010-03-23 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6273617> Linker warning not shown in the Xcode build log - * src/Options.cpp: add colon to format string in warning() + * <rdar://problem/7783918> new linker makes dylibs with no __text section, causing codesign_allocate tool to fail + * make sure there is always a __text section in dylibs and bundles +-------- tagged ld64-114.5 ------ Tagged ld64-89.3 +2010-03-22 Nick Kledzik <kledzik@apple.com> -2008-10-24 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7767311> missing __objc_imageinfo section + * Real fix will be in 7780438. For now have Resolver also accumulate objc constraint info - <rdar://problem/6317985> ld64-89 broke TOT OpenGL libProgrammability x86_64 build - * src/MachOReaderRelocatable.hpp: add cast in getEncodedP() - - ------ Tagged ld64-89.2 -2008-10-23 Nick Kledzik <kledzik@apple.com> +2010-03-19 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6312895> SnowLeopard: Libsystem built with ld64-89.1 causes crashes - * src/MachOReaderRelocatable.hpp: when FDE information causes __text atom to be split, make the - atoms follow-on pairs. + * <rdar://problem/7772740> ld64-114 does not error on missing exported symbols with -dead_strip - ------ Tagged ld64-89.1 - -2008-10-22 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: for x86_64 __eh_frame force direct references +-------- tagged ld64-114.4 +2010-03-16 Nick Kledzik <kledzik@apple.com> -2008-10-21 Nick Kledzik <kledzik@apple.com> - - * src/ObjectDump.cpp: Use getContentType() to see if content type is a cstring + * <rdar://problem/7762146> dyld missing LC_ID_DYLINKER +-------- tagged ld64-114.3 ------ Tagged ld64-89 +2010-03-15 Nick Kledzik <kledzik@apple.com> -2008-10-21 Nick Kledzik <kledzik@apple.com> + * force i386 kexts to be built with ld_classic + * preserve 'l' labels in static executables + * sync section offsets and addresses in segments with command line addresses - <rdar://problem/6253171> 10A180 with QT-1119 roots: iTunes and QuickTime cannot play back purchased videos - <rdar://problem/5950453> linker should not need .eh labels - * src/MachOWriterExecutable.hpp: use kCFIType to set section attributes - * src/MachOReaderRelocatable.hpp: use libunwind's CFITuple to parse __eh_frame content - * src/ld.cpp: Add adjustScope() phase instead of demoting scope within symboltable.add() - * unit-tests/test-cases/eh-stripped-symbols: added test case +-------- tagged ld64-114.2 ------ Tagged ld64-88.1 +2010-03-13 Nick Kledzik <kledzik@apple.com> -2008-10-16 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7751930> ld64-114 generates x86_64 kext external call sites with incorrect addend - * src/MachOReaderRelocatable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT - * src/MachOWriterExecutable.hpp: Fix uses of COMPACT_UNWIND_SUPPORT +-------- tagged ld64-114.1 +2010-03-12 Nick Kledzik <kledzik@apple.com> -2008-09-30 Nick Kledzik <kledzik@apple.com> + * Fix dyldinfo tool to correct show ordinal info for classic linkedit - <rdar://problem/6255983> OBJC2: Reorder __DATA,__objc_* sections by writedness - * src/ld.cpp: change sorting order of Sections +2010-03-12 Nick Kledzik <kledzik@apple.com> + <rdar://problem/7748047> ld64-114 is causing read_only_reloc verification errors for ppc + * Update machochecker to check this. -2008-09-29 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-114 - <rdar://problem/6159479> Executable produced by XCode 3.2 on 10.6 crashes on 10.3.9 - * src/MachOWriterExecutable.hpp: set objc_module_info_addr field of module table - - ------ Tagged ld64-88 - -2008-09-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5935708> kexts need to be built as MH_BUNDLE mach-o files - * src/ld.cpp: use getUndefinedProxyAtom() with kKextBundle - * src/MachOFileAbstraction.hpp: add MH_KEXT_BUNDLE - * src/Options.cpp: support -kext for all architectures - * src/MachOWriterExecutable.hpp: support kKextBundle to make a bundle like kext - * unit-tests/test-cases/kext-basic: added test case - - -2008-09-25 Nick Kledzik <kledzik@apple.com> +2010-03-11 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6052546> ld invoking wrong ld_classic - * src/Options.cpp: first look for ld_classic relative to ld itself - + * <rdar://problem/7744174> i386 dylibs built with ld64-112 cause runtime errors when incorporated into the dyld shared cache + * Add -shared_region option to dyldinfo tool -2008-09-25 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-113 - <rdar://problem/5855588> ld fails to link references from 32 bit code into 64 bit code - <rdar://problem/5955200> Desired 32-bit absolute relocation - * src/Architectures.hpp: add x86_64::kPointer32 - * src/MachOReaderRelocatable.hpp: support X86_64_RELOC_UNSIGNED with length=2 - * src/MachOWriterExecutable.hpp: support x86_64::kPointer32 - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added 32-bit pointer tests - +2010-03-11 Nick Kledzik <kledzik@apple.com> -2008-09-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6075323> Should be able to mark dylibs as auto-dead-dylib-strip - * src/Options.h: add fMarkDeadStrippableDylib - * src/MachOReaderDylib.hpp: check MH_DEAD_STRIPPABLE_DYLIB - * src/ObjectFile.h: add deadStrippable() - * src/MachOFileAbstraction.hpp: add MH_DEAD_STRIPPABLE_DYLIB - * src/Options.cpp: support -mark_dead_strippable_dylib - * src/MachOWriterExecutable.hpp: test reader->deadStrippable(), set MH_DEAD_STRIPPABLE_DYLIB - * doc/man/man1/ld.1: update man page - * unit-tests/test-cases/dead_strippable_dylib: added test case - + * Allow CPU_SUBTYPE_ARM_ALL .o files to be linked into any arm arch linkage -2008-09-25 Nick Kledzik <kledzik@apple.com> +2010-03-11 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6197601> ER: Add -seg_page_size option - * src/Options.cpp: add -seg_page_size option - * src/MachOWriterExecutable.hpp: use new page size info when laying out segments - * doc/man/man1/ld.1: update man page - + <rdar://problem/7741238> ld64-112 with -undefined dynamic_lookup marks all symbols as being flat + * update test case at: unit-tests/test-cases/undefined-dynamic-lookup -2008-09-24 Nick Kledzik <kledzik@apple.com> +2010-03-10 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5798786> -arch_errors_fatal not working - * src/ld.cpp: check fOptions.errorOnOtherArchFiles() - * src/Options.cpp: turn -arch_errors_fatal into fOptions.errorOnOtherArchFiles() + * prevent possible crash in warning about can't export hidden symbols +2010-03-10 Nick Kledzik <kledzik@apple.com> -2008-09-24 Nick Kledzik <kledzik@apple.com> + * make sure split-info data is zero terminated - <rdar://problem/6161215> CrashTracer: [USER] 1 crash in ld at ld: 0x5ce02 - * src/ld.cpp: abort if resolve() finds an unresolved reference, rather than allow a future crash - +-------- tagged ld64-112 -2008-09-24 Nick Kledzik <kledzik@apple.com> +2010-03-09 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6157989> linker crashes linking X86-64 with -fwritable-strings - * src/MachOReaderRelocatable.hpp: handle unbound cfstring references - * unit-tests/test-cases/cfstring-coalesce: update test case - + * Never dead strip sections added with -sectcreate + * Added test case: unit-tests/test-cases/sectcreate-dead_strip -2008-09-24 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6213035> ld64: bl out of range (-17147704 max is +/-16M) on ppc - * src/MachOWriterExecutable.hpp: tweak branch island regions to be every 14MB instead of 15MB - +-------- tagged ld64-111 -2008-09-24 Nick Kledzik <kledzik@apple.com> +2010-03-03 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5907981> -filelist fails with comma in path - * src/Options.cpp: in loadFileList() first try without special comma meaning - * unit-tests/test-cases/filelist/Makefile: update test case - + * Add support for "-arch arm -force_cpusubtype_ALL" to keep gcc building -2008-09-23 Nick Kledzik <kledzik@apple.com> +2010-03-02 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6203068> nop not used when aligning functions in -r mode - * src/MachOWriterExecutable.hpp: change check for when to pad with nops to not test segment's name - + * Add some checking to the use of upward dylibs -2008-09-23 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-110 - <rdar://problem/6238329> "-pie can only be used when linking a main executable" should be a warning, not an error - * src/Options.cpp: make -pie on a dylib or bundle be a warning instead of an error - - -2008-09-23 Nick Kledzik <kledzik@apple.com> +2010-03-01 Nick Kledzik <kledzik@apple.com> - * src/MachOReaderRelocatable.hpp: add warning if dwarf cannot be encoded as compact unwind - + * Don't coalesce cstrings across segments -2008-09-18 Nick Kledzik <kledzik@apple.com> +2010-03-01 Nick Kledzik <kledzik@apple.com> - * src/LTOReader.hpp: re-enable use of lto_codegen_debug_options() + * Emulate previous linker bug where hidden symbols with dynamically-referenced + bit were promoted to global. + * Added test case: unit-tests/test-cases/unstrippable-symbols +-------- tagged ld64-109.1 -2008-09-16 Nick Kledzik <kledzik@apple.com> +2010-02-26 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6219054> ld does not always set S_CSTRING_LITERALS on __TEXT,__cstring - * src/MachOReaderRelocatable.hpp: add getContentType() to SymbolAtom - * src/MachOWriterExecutable.hpp: for x86_64 don't override named cstrings with LC* name + * Make sure building dyld results in a thread load command +-------- tagged ld64-109 -2008-09-10 Nick Kledzik <kledzik@apple.com> - - * Options.cpp: add __crashreporter_info__ to communicate command line to crash reporter - * ld64.xcodeproj/project.pbxproj: leave local symbols in ld to provide better crash reports - +2010-02-26 Nick Kledzik <kledzik@apple.com> -2008-09-08 Nick Kledzik <kledzik@apple.com> + * Better sorting of zero-fill sections to preserve discovery order + * Zero out file offsets in dynamic symbol table entries that are not used - <rdar://problem/6126637> 161569 GCC 4.2 - breakpoints no longer work for a large number of functions - * src/MachOReaderRelocatable.hpp: support DW_FORM_strp out-of-line strings when parsing line table +2010-02-26 Nick Kledzik <kledzik@apple.com> + * Support pointer-diffs to zero sized atom in zero sized section -2008-09-02 Nick Kledzik <kledzik@apple.com> +2010-02-25 Nick Kledzik <kledzik@apple.com> - * src/MachOReaderRelocatable.hpp: fix compact unwind personality for dyld and -slow_stubs - + * Add support for -r mode with ppc64 -2008-08-29 Nick Kledzik <kledzik@apple.com> +2010-02-25 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6186838> -weak_library no longer forces uses to be weak_import - * src/MachOWriterExecutable.hpp: use fWeakImport on dylib to force proxy atoms into fWeakImportMap - * unit-tests/test-cases/weak_import-force: added test case + * Handle multiple labels on the same coalesable literal by making an + atom for each label, each with the same content. + * Add test case: unit-tests/test-cases/literals-labels -2008-08-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6061558> linker should order __DATA segment to reduce dyld dirtied pages - * src/Options.cpp: add fOrderData and support -no_data_order - * src/ld.cpp: modify tweakLayout() to sort atoms with relocations to start of __data section +2010-02-25 Nick Kledzik <kledzik@apple.com> + * Handle old ppc .o files that have stubs to static functions -2008-08-27 Nick Kledzik <kledzik@apple.com> +2010-02-25 Nick Kledzik <kledzik@apple.com> - * src/Options.cpp: back out <rdar://problem/6174493> + * Add basic ppc64 support +2010-02-24 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-87.5 + * Range check TOC entries in archives -2008-08-26 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6174493> some projects show _Unwind_Resume coming from libSystem.B.dylib - * src/Options.cpp: swap any early symlinks to libSystem with libgcc_s +2010-02-23 Nick Kledzik <kledzik@apple.com> + * Fix spurious dylib re-export warnings that are just regular linkage cycles ------ Tagged ld64-87.4 -2008-08-25 Nick Kledzik <kledzik@apple.com> +2010-02-23 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6174493> some projects show _Unwind_Resume coming from libSystem.B.dylib - * src/Options.cpp: swap any early libSystem with libgcc_s - + * re-partition bits in mach_o::relocatable::Atom ivars to allow more functions per file -2008-08-15 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5898326> Unable to build ppc debug builds (linker out of range error) - * src/MachOWriterExecutable.hpp: in addPPCBranchIslands() look ahead so large atoms don't push out branch islands +2010-02-22 Nick Kledzik <kledzik@apple.com> + * re-partition bits in mach_o::relocatable::Atom ivars to allow more fixups per function ------ Tagged ld64-87.3.1 - -2008-09-08 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6204202> i386 dylibs have incorrect personality pointers when put in dyld shared cache - * src/MachOWriterExecutable.hpp: in addCrossSegmentRef() handle kImageOffset32 to __IMPORT segment +2010-02-22 Nick Kledzik <kledzik@apple.com> + * Handle re-exported dylibs that are re-exported again + * Added test case: unit-tests/test-cases/re-export-layers ------ Tagged ld64-87.3 -2008-08-09 Nick Kledzik <kledzik@apple.com> +2010-02-22 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6125381> work around compiler gcc_except_table alignment - * src/ObjectFile.h: change getLSDA() to return a reference instead of an atom - * src/MachOReaderRelocatable.hpp: special case __eh_frame 64-bit pointer diff relocations - * src/MachOWriterExecutable.hpp: track lsda offset when creating __unwind_info section - * src/UnwindDump.cpp: log when LDSA content does not start with 0xFF - ------ Tagged ld64-87.2 - -2008-08-07 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6131559> 10A141: libuwind falls back to dwarf and makes whole system super slow - * src/MachOWriterExecutable.hpp: Fix sign extension bug with x86_64::kPointerDiff24 - * src/UnwindDump.cpp: warn about mangled LSDA entries when dumping unwind section + * Properly handle X86_64_RELOC_SUBTRACTOR with non-external target symbol ------ Tagged ld64-87.1 - -2008-08-03 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-108 - * src/LTOReader.hpp: Don't use lto_codegen_debug_options until newer libLTO.dylib is available +2010-02-17 Nick Kledzik <kledzik@apple.com> - ------ Tagged ld64-87 - -2008-07-21 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: Always set fAutoOrderInitializers=false for dyld + * <rdar://problem/7658740> ER: Support upward dylib dependencies + * Add test case: unit-tests/test-cases/dylib-upward -2008-07-21 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix when regular vs compressed __unwind_info pages are generated - * src/UnwindDump.cpp: fix function name decoding in regular pages - - -2008-07-21 Nick Kledzik <kledzik@apple.com> +2010-02-17 Nick Kledzik <kledzik@apple.com> - * ld64.xcodeproj/project.pbxproj: don't allow ld to build for x86_64 until libdtrace.dylib is available + * <rdar://problem/7401775> ld(1) man page typo + * <rdar://problem/7230340> Linker (ld) man page typo: "unredable" in -pagezero_size option description + * <rdar://problem/7207483> Typo in ld(1) man page, "-x" option + * <rdar://problem/6239264> man ld: Change "if" -> "is" + * <rdar://problem/7393216> DOC: ld(1) mentions -dynamiclib when it means -dylib -2008-07-18 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: don't crash if debug_line section has no line table - - -2008-07-18 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5628149> Duplicate probe firings in Security.framework - * src/LTOReader.hpp: optimize() now returns atoms optimized away - * src/ObjectFile.h: optimize() should return if it did anything - * src/ArchiveReader.hpp: pass through optimize() result - * src/ld.cpp: rework dtrace probe processing as a new pass to prevent double counting - - -2008-07-15 Nick Kledzik <kledzik@apple.com> +2010-02-17 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6061904> automatically order initializers to start of __TEXT - * src/Options.cpp: add -no_order_inits option - * src/MachOReaderRelocatable.hpp: merge __StaticInit into __text - * src/ObjectFile.h: add fAutoOrderInitializers - * src/ld.cpp: sort initializer to start of __text and terminators to end - * doc/man/man1/ld.1: add doc about -no_order_inits - * unit-tests/test-cases/init-order: add test case + * <rdar://problem/7625461> Wordsmith ld warning about missing directories -2008-07-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6073697> Only add LC_SEGMENT_SPLIT_INFO to dylibs that might be in the shared cache - * src/MachOWriterExecutable.hpp: re-layout load commands after split-seg data computed - * src/Options.cpp: non-public install name will disable split-seg load command - - -2008-07-14 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6069861> ld -r for x86_64 is changing visibility of cstring constants - * src/MachOWriterExecutable.hpp: force x86_64 cstring labels to be local in -r mode - * unit-tests/test-cases/cstring-label: added test case - -2008-07-11 Nick Kledzik <kledzik@apple.com> +2010-02-17 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6070190> ld not adding updating LC_SEGMENT_SPLIT_INFO with __unwind_info section - * src/MachOWriterExecutable.hpp: run createSplitSegContent() after __unwind_info section is created + * Fix -umbrella to work when umbrella is a dylib instead of a framework + * Add test case: unit-tests/test-cases/umbrella-dylib -2008-07-10 Nick Kledzik <kledzik@apple.com> - - * src/LTOReader.hpp: improve missing symbol error message +-------- tagged ld64-107 -2008-07-09 Nick Kledzik <kledzik@apple.com> +2010-02-16 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6061558> linker should order __DATA segment to reduce dyld dirtied pages - * src/ld.cpp: first phase, order sections + * Fix bugs with -preload -2008-07-08 Nick Kledzik <kledzik@apple.com> +2010-02-16 Nick Kledzik <kledzik@apple.com> - * src/MachOReaderRelocatable.hpp: remove "coal" sections when creating a final linked image + * Fix dylib re-export cylce detection -2008-07-08 Nick Kledzik <kledzik@apple.com> +2010-02-16 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6054476> ld: add support for mllvm LTO options - * src/Options.cpp: support -mllvm option - * src/LTOReader.hpp: call lto_codegen_debug_options() with -mllvm options - * src/ld.cpp: pass llvmOptions to optimize() - * src/Options.h: add fLLVMOptions - * src/ArchiveReader.hpp: add llvmOptions parameter to optimize() - * src/ObjectFile.h: add llvmOptions parameter to optimize() - * unit-tests/test-cases/lto-llvm-options: add test case + * <rdar://problem/6018216> -ObjC not pulling in members with categories only + * scan for non-zero __objc_catlist section in archive members when -ObjC is used + * Added test case: unit-tests/test-cases/objc-category-archive -2008-07-07 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6054296> Linker fails with: 24-bit pointer diff out of range in unwind info in unwind info from... - * src/MachOWriterExecutable.hpp: fix when to fallback to uncompressed unwind info - +2010-02-15 Nick Kledzik <kledzik@apple.com> -2008-07-03 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7398610> ld glibly removes /dev/null - <rdar://problem/6001472> ld crash with gcc-4.0 code that uses a zero sized array - * src/MachOReaderRelocatable.hpp: handle zero size atom in a zero sized section +2010-02-15 Nick Kledzik <kledzik@apple.com> -2008-07-03 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7196255> Linker should be able to validate GC intentions + * Add -objc_gc and -objc_gc_only. Error when used and RR based .o file is linked in + * Update test case: unit-tests/test-cases/objc-gc-checks - <rdar://problem/6016809> ld crashes when bad ppc relocs are found - * src/MachOReaderRelocatable.hpp: change all missing PAIR warnings to errors - -2008-07-02 Nick Kledzik <kledzik@apple.com> +2010-02-15 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5881324> when linking a kext the static linker should leave a pad in the headers to allow code signing - * src/MachOWriterExecutable.hpp: add padding for load commands in object files - * unit-tests/test-cases/code-signed-object-file: added test case + * <rdar://problem/7421695> Linker should provide a way to mark binaries that support compaction + * Added -objc_gc_compaction option + * Update test case: unit-tests/test-cases/objc-gc-checks -2008-07-02 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6048484> LC_SEGMENT_64 filesize incorrect for MH_OBJECT filetype - * src/MachOWriterExecutable.hpp: correctly set segment size info in object files - * unit-tests/test-cases/no-object-symbols: add test case - - -2008-06-26 Nick Kledzik <kledzik@apple.com> +2010-02-15 Nick Kledzik <kledzik@apple.com> - * ld64.xcodeproj/project.pbxproj: enable ld and rebase targets to build for x86_64 - * src/rebase.cpp: remove unused fRelocBase field that was not 64-bit clean - * src/MachOReaderRelocatable.hpp: fix getEncodedP() to be 64-bit clean + * <rdar://problem/7546367> ER: Need a way to detect weak exports in dev tools + * implement -warn_weak_exports +2010-02-15 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-86.3 - -2008-06-17 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: fix loadUndefines() to double check undefine symbol was not already loaded - - ------ Tagged ld64-86.2 - -2008-06-14 Nick Kledzik <kledzik@apple.com> + * Add support for LD_DYLD_PATH - * srd/ld.cpp: Add NULL check in getTentativesNames() - - ------ Tagged ld64-86.1 -2008-06-06 Nick Kledzik <kledzik@apple.com> +2010-02-15 Nick Kledzik <kledzik@apple.com> - * src/MachOWriterExecutable.hpp: fix header padding calculation for dyld + * <rdar://problem/7636072> cfstring backing store points to global cstring + * Force all by-name references in cfstring to be direct references + * add test case: unit-tests/test-cases/cfstring-and-cstring ------ Tagged ld64-86 +-------- tagged ld64-106 -2008-06-04 Nick Kledzik <kledzik@apple.com> +2010-02-12 Nick Kledzik <kledzik@apple.com> - * src/LTOReader.hpp: if lto_codegen_add_module() fails, add explanation to error message + * <rdar://problem/7644673> Assertion failed: when class is translation unit scoped + * added test case unit-tests/test-cases/objc-visibility -2008-06-04 Nick Kledzik <kledzik@apple.com> - - * src/ObjectFile.h: add deadAtoms parameter to optimize() - * src/ld.cpp: ditto - * src/ArchiveReader.hpp: ditto - * src/MachOReaderRelocatable.hpp: handle llvm use of 0x1B pointer encodings in CIEs - * src/LTOReader.hpp: make sure libLTO.dylib knows about any llvm symbol coalesced away - * unit-tests/test-cases/lto-weak-native-override: add test case +2010-02-12 Nick Kledzik <kledzik@apple.com> + * <rdar://problem/7644828> crash with missing crt? -2008-06-04 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5935600> LTO : 176.gcc and 177.mesa build failure at -O4 - * src/LTOReader.hpp: make sure internal is returned by getAtoms() - * unit-tests/test-cases/lto-archive-dylib: update test case +2010-02-12 Nick Kledzik <kledzik@apple.com> + * Suppress indirect symbol table in static executables + -2008-06-03 Nick Kledzik <kledzik@apple.com> +2010-02-12 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5894163> fix for 5613343 need to search for definitions for common symbols is broken - * src/ld.cpp: modify loadUndefines() to check for undefines in all files and tentative definitions but only in archives - * src/machochecker.cpp: check for undefine symbols and external symbols with same name - * unit-tests/test-cases/tentative-and-archive: update test case + * Rework CIE parsing to work with icc generated code -2008-06-03 Nick Kledzik <kledzik@apple.com> +2010-02-11 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5935237> linker produces wrong result for 16-bit call relocations - * src/MachOReaderRelocatable.hpp: properly parse i386 scattered relocs for word sized pc-rel vanilla - * src/MachOWriterExecutable.hpp: propery compute displacement for x86::kPCRel16 - * unit-tests/test-cases/relocs-asm: update test case with callw instructions - + * Fix creation of debug notes + * Tweak unit-tests/test-cases/dwarf-debug-notes to match llvm symbol layout -2008-06-03 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5905900> Building kext x86_64 with unexported symbols file causes linking problems - * src/MachOWriterExecutable.hpp: better check when creating undefined proxy atoms - * unit-tests/test-cases/unexported_symbols_list-r: added test case - +2010-02-11 Nick Kledzik <kledzik@apple.com> -2008-06-02 Nick Kledzik <kledzik@apple.com> + * Don't assert when infering ppc subtype that is ALL. + * Fix spurious warning about mismatched subtypes, when subtype is inferred - <rdar://problem/5659338> S_CSTRING_LITERALS section type not preserved in executable - * src/ObjectFile.h: added ContentType - * src/MachOReaderRelocatable.hpp: set ContentType for anonymous string literals - * src/MachOWriterExecutable.hpp: set S_CSTRING_LITERALS if ContentType is kCStringType - * unit-tests/test-cases/cstring-custom-section: added test case - -2008-06-02 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5980194> linker should produce __unwind_info section in final linked images - * src/ld.cpp: sort __unwind_info then __eh_frame section to end of __TEXT - * src/Architectures.hpp: add kImageOffset32 and kPointerDiff24 - * src/ObjectFile.h: add compact unwind info support - * src/MachOReaderRelocatable.hpp: add compact unwind info support - * src/MachOFileAbstraction.hpp: add C++ wrappers for unwind section layout - * src/UnwindDump.cpp: new tool for dumping __unwind_info section - * src/MachOWriterExecutable.hpp: create __unwind_info section when needed - * src/ObjectDump.cpp: print unwind info - +-------- tagged ld64-105 -2008-06-02 Nick Kledzik <kledzik@apple.com> +2010-02-11 Nick Kledzik <kledzik@apple.com> - * unit-tests/test-cases/llvm-integration: split out some test cases - * unit-tests/test-cases/lto-preload-pie: added - * unit-tests/test-cases/lto-archive-dylib: added + * Use symbolic constants for bit field sizes -2008-05-30 Nick Kledzik <kledzik@apple.com> +2010-02-10 Nick Kledzik <kledzik@apple.com> - * unit-tests: fixes to build all tests with with gcc-4.2 on SnowLeopard - + * Handle out of order sections in .o files -2008-05-30 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5536348> support -preload option to generate MH_PRELOAD binaries compatible with mtoc(1) and EFI - * src/ld.cpp: add entryPoint parameter to optimize() - * src/ArchiveReader.hpp: ditto - * src/ObjectFile.h: ditto - * src/LTOReader.hpp: use entryPoint parameter to optimize() - * src/Options.h: add kPreload and segment alignment - * src/Options.cpp: support -preload and -segalign - * src/MachOWriterExecutable.hpp: support kPreload and non-page aligned segments +-------- tagged ld64-104 +2010-02-10 Nick Kledzik <kledzik@apple.com> -2008-05-30 Nick Kledzik <kledzik@apple.com> + * Rename __tentative internal section name to __comm/tent to sort like __common + * Fix test case in unit-tests/test-cases/tentative-to-real-r - <rdar://problem/4431008> ld should warn if passed -r and also dylibs - * src/ld.cpp: check for spurious dylibs in Linker::addDylib() +2010-02-10 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-85.6 + * Better warning messages about mismatched architectures -2008-11-01 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6254202> support increased branch range in Thumb-2 - * src/MachOWriterExecutable.hpp: in fixUpReferenceFinal() support new longer branch range - -2008-11-01 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/6104368> ld warning: unknown option to -iphoneos_version_min, not 1.x or 2.x - * src/Options.cpp: In setIPhoneVersionMin() support 3.x +2010-02-10 Nick Kledzik <kledzik@apple.com> - ------ Tagged ld64-85.5 + * Gracefully ignore if there are >8000 line info per function in debug info -2008-09-17 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6162415> vtable pointers can be missing thumb bit - * src/MachOWriterExecutable.hpp: Writer<arm>::fixUpReferenceFinal() OR in the 1 bit if the target - of a arm::kReadOnlyPointer is thumb. +2010-02-10 Nick Kledzik <kledzik@apple.com> + * Properly handle when regular definition is weak_imported + * Add unit-tests/test-cases/weak_import-local ------ Tagged ld64-85.4 -2008-08-11 Nick Kledzik <kledzik@apple.com> +2010-02-10 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6138961> ld should ignore LD_PREBIND when processing a static archive - * src/MachOWriterExecutable.hpp: in setImportNlist() never use N_PBUD for object files + * Don't try to coalesce zero length cstrings in mach-o parser. ------ Tagged ld64-85.3 -2008-07-14 Nick Kledzik <kledzik@apple.com> +2010-02-10 Nick Kledzik <kledzik@apple.com> - <rdar://problem/6060912> Prebinding busted in DTSB - * src/Options.cpp: check for libstdc++.6.0.[49] in seg_addr_table + * Add work around for llvm using L labels for backing string of CFString with -fwritable-strings ------ Tagged ld64-85.2 - -2008-05-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5905889> ARM ld should take W bit off of maxprot for __TEXT segment - * src/MachOWriterExecutable.hpp: for iPhone always set maxprot to be initprot in all segments +2010-02-09 Nick Kledzik <kledzik@apple.com> + * Ignore labels in __gcc_except_tab section + * Properly apply local relocations to __eh_frame section so CFI parser works + * Update unit-tests/test-cases/eh-stripped-symbols to reproduce problem -2008-05-06 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5914343> encryptable images may not be signable - * src/MachOWriterExecutable.hpp: use minimum header padding when aligning __text section - - ------ Tagged ld64-85 (Xcode 3.1) - -2008-04-29 Nick Kledzik <kledzik@apple.com> - - * ld64.xcodeproj/project.pbxproj: <llvm-c/lto.h> is moving from /usr/local/include to /Developer/usr/local/include +2010-02-09 Nick Kledzik <kledzik@apple.com> + * Fix file offset computation with large zero-fill sections + -2008-04-29 Nick Kledzik <kledzik@apple.com> +2010-02-09 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5829579> ld doesn't honor "rightmost" -syslibroot argument - * src/Options.cpp: if last -syslibroot is /, then ignore all syslibroots + * Force global 'l' to be hidden + * Add test case with objc properties: unit-tests/test-cases/objc-properties -2008-04-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5866582> GLRendererFloat has bad __eh_frame section caused by mixing llvm-gcc and gcc object files - * src/MachOReaderRelocatable.hpp: make all atoms in __eh_frame section have 1-byte alignment - * src/MachOWriterExecutable.hpp: make __eh_frame section have pointer sized alignment +-------- tagged ld64-103 +2010-02-04 Nick Kledzik <kledzik@apple.com> -2008-04-17 Nick Kledzik <kledzik@apple.com> + * Temporarily change assert() to call exit(1) instead of abort() - * src/MachOReaderRelocatable.hpp: better cpu subtype support +2010-02-04 Nick Kledzik <kledzik@apple.com> -2008-04-14 Nick Kledzik <kledzik@apple.com> + * Fix another case in -r mode where the vmsize was less that filesize - <rdar://problem/5733759> ld64 has bad ARM branch island check - * src/MachOWriterExecutable.hpp: in addBranchIslands() don't force large arm programs to fail +2010-02-04 Nick Kledzik <kledzik@apple.com> -2008-04-10 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix stubs used with lazy dylibs + * Fix assert when generating GSYM stab debug notes ------ Tagged ld64-84.4 - -2008-04-10 Nick Kledzik <kledzik@apple.com> +2010-02-04 Nick Kledzik <kledzik@apple.com> - <rdar://problem/5847206> SPEC2000/eon built with -mdynamic-no-pic won't run - * src/Architectures.hpp: added arm::kReadOnlyPointer - * src/MachOReaderRelocatable.hpp: generate arm::kReadOnlyPointer - * src/MachOWriterExecutable.hpp: use arm::kReadOnlyPointer - * src/machochecker.cpp: allow MH_PIE bit - * unit-tests/test-cases/switch-jump-table: added test cases - - ------ Tagged ld64-84.3 + * Add SRCROOT to crash logs -2008-04-09 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5852023> -undefined dynamic_lookup busted - * src/ld.cpp: don't create proxy atom when scanning for dylib duplicates - * unit-tests/test-cases/tentative-and-archive: use -undefined dynamic_lookup +2010-02-04 Nick Kledzik <kledzik@apple.com> ------ Tagged ld64-84.2 + * Remove architectureName() from InputFiles -2008-04-04 Nick Kledzik <kledzik@apple.com> - * src/ld.cpp: don't add .eh symbols to symbol table in -r mode - * unit-tests/test-cases/eh-coalescing-r: update to test out of order coalescing - +-------- tagged ld64-102 ------ Tagged ld64-84.1 +2010-02-03 Nick Kledzik <kledzik@apple.com> -2008-03-28 Nick Kledzik <kledzik@apple.com> + * Add follow-on reference from symbol text atom to non-symboled text atom - <rdar://problem/5720961> ld should prefer architecture-specific variant over generic in fat object file - * src/Options.cpp: fully process -arch arguments into fArchitecture and fSubArchitecture - * src/ld.cpp: when -arch with a subtype is used, try to find the exact subtype from fat files - * unit-tests/test-cases/cpu-sub-types-preference: added test cases for arm and ppc - ------ Tagged ld64-84 +-------- tagged ld64-101 -2008-03-28 Nick Kledzik <kledzik@apple.com> +2010-01-29 Nick Kledzik <kledzik@apple.com> - * src/LTOReader.hpp: don't print lto version, if lto is unavailable + * fix -alias symbols to be global by default -2008-03-26 Nick Kledzik <kledzik@apple.com> +-------- tagged ld64-100 - <rdar://problem/5575399> Add LD_WARN_COMMONS to BigBear builds - * src/Options.cpp: Add support for LD_WARN_FILE which copies all warnings to a side file +2010-01-28 Nick Kledzik <kledzik@apple.com> - -2008-03-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5797713> Need encryption tag in mach-o file - <rdar://problem/5811920> linker should adjust arm final linked images so __text is never on the same page as the load commands - * src/MachOFileAbstraction.hpp: add support for encryption_info_command - * src/Options.cpp: add support for LD_NO_ENCRYPT and -no_encryption - * src/MachOWriterExecutable.hpp: add EncryptionLoadCommandsAtom - * src/machochecker.cpp: validate LC_ENCRYPTION_INFO + * Merge new/refactored linker to trunk - -2008-03-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5712533> ld64 does not recognize LLVM bitcode archive files - * src/MachOReaderArchive.hpp: renamed to src/ArchiveReader.hpp - * src/ArchiveReader.hpp: sniff each member and instantiate correct reader - * src/ld.cpp: rename mach_o::archive::Reader to archive::Reader - * ld64.xcodeproj/project.pbxproj: rename MachOReaderArchive.hpp to ArchiveReader.hpp - * unit-tests/test-cases/llvm-integration: added test case - - -2008-03-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5771658> ld64 should switch to new libLTO.dylib interface - <rdar://problem/5675690> Produce llvm bc file in 'ld -r' mode if all .o files are llvm bc - * src/LTOReader.hpp: rewrite from LLVMReader.hpp to use new lto_* C interface - * unit-tests/test-cases/llvm-integration: update and comment - * ld64.xcodeproj/project.pbxproj: update to lazy load libLTO.dylib - * src/ld.cpp: rework and simplify Linker::optimize() - * src/ObjectDump.cpp: Add -nm option - - -2008-03-25 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.cpp: Fix some .objc_class_name_ off by one problem - * src/MachOWriterExecutable.cpp: Fix some .objc_class_name_ off by one problem - - -2008-03-24 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5814613> Xcode 3.1 breaks linkage of libgcj.9.dylib from gcc 4.3.0 - * src/MachOWriterExecutable.cpp: Make sure all ivars in Writer are initialized. - - -2008-03-21 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: warn if -seg1addr value is not page aligned - - -2008-03-21 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5806437> Move ARM support outside of __OPEN_SOURCE__ - * src/ld.cpp: remove __OPEN_SOURCE__ around arm support - * src/LLVMReader.hpp: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderDylib.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectFile.h: remove __OPEN_SOURCE__ around arm support - * src/MachOReaderRelocatable.hpp: remove __OPEN_SOURCE__ around arm support - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: remove __OPEN_SOURCE__ around arm support - * src/ObjectDump.cpp: remove __OPEN_SOURCE__ around arm support - * ld64.xcodeproj/project.pbxproj: remove ARM_SUPPORT from config.h - - ------ Tagged ld64-83.2 - -2008-03-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5801620> ld64-83 removes OBJC_CLASS_$ symbols from projects, causes catastrophic results - * src/Options.cpp: restore "case CPU_TYPE_ARM" in switch statement for .objc_class symbols in .exp files - * unit-tests/test-cases/objc-exported_symbols_list: added test case - - ------ Tagged ld64-83.1 - -2008-03-14 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5800466> -iphone_version_min ==> -iphoneos_version_min - * src/Options.cpp: support -iphoneos_version_min as well - - ------ Tagged ld64-83 - -2008-03-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5791543> ld needs to strip iphone_version_min option if invoking ld_classic - * src/Options.cpp: suppress -iphone_version_min from being passed to ld_classic - - -2008-03-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4171253> ADOBE XCODE: Linker option to lazy load frameworks (cause dyld is too slow) - * src/MachOWriterExecutable.hpp: create lazy stubs and LC_LAZY_LOAD_DYLIB for lazy load dylibs - * src/Options.cpp: support -lazy-l, -lazy_library, and -lazy_framework - * src/MachOFileAbstraction.hpp: add LC_LAZY_LOAD_DYLIB and S_LAZY_DYLIB_SYMBOL_POINTERS until in cctools - * src/MachOReaderDylib.hpp: add isLazyLoadedDylib() - * src/ld.cpp: pass lazy helper atom to writer - * doc/man/man1/ld.1: document new options - * unit-tests/test-cases/lazy-dylib-objc: add test case - * unit-tests/test-cases/lazy-dylib: add test case - - ------ Tagged ld64-82.7 - -2008-03-07 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5787149> duplicate symbol literal-pointer@__OBJC@__message_refs@... - * src/MachOReaderRelocatable.hpp: AnonymousAtom from S_LITERAL_POINTERS section should be weak - * unit-tests/test-cases/objc-selector-coalescing: added test case - - ------ Tagged ld64-82.6 - -2008-03-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5779681> ld crashes building XsanFS for Snow Leopard Builds - * src/ld.cpp: add bool dylibsOnly parameter to addJustInTimeAtoms() - * unit-tests/test-cases/tentative-and-archive: added test case - -2008-03-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5775822> ld64 should not force building with gcc 4.0 - * ld64.xcodeproj/project.pbxproj: change rules to use "system" compiler instead of 4.0 - - -2008-02-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5774730> Simulator frameworks are being build split-seg and not prebound - * src/Options.cpp: only splitseg if prebound - - -2008-02-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5774231> Linker should not make GSYM debug note for .objc_category_* symbols - * src/ld.cpp: suppress GSYM debug notes for absolute symbols - * unit-tests/test-cases/objc-category-debug-notes: added test case - - -2008-02-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5768970> non-ASCII CFString support is broken - * src/MachOReaderRelocatable.hpp: only name and coalesce cfstring constants if they use a __cstring - * unit-tests/test-cases/cfstring-utf16: add test case - - -2008-02-25 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5366363> ld -r -x - * doc/man/man1/ld.1: update man page to explain -r -x produces auto-stripped labels - - ------ Tagged ld64-82.5 - -2008-02-12 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5738023> x86_64: -stack_size failure when large __bss is used - * src/ld.cpp: only move section already in __DATA segment to new __huge section - * unit-tests/test-cases/stack_size_no_addr: updated test case to add large bss section - - ------ Tagged ld64-82.4 - -2008-02-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5726215> comdat warnings with ld -r of C++ .o files - * unit-tests/test-cases/eh-coalescing-r: added test case - * src/ld.cpp: in ld -r mode don't warn about if .eh symbols are not static - - -2008-02-06 Devang Patel <dpatel@apple.com> - - <rdar://problem/5724990> LTO of Bom framework with -dead_strip causes ld(1) crash - * src/LLVMReader.hpp: Check fAtom while determining LLVMReference target binding. - * unit-tests/test-cases/llvm-integration/Makefile: Add new test case. - * unit-tests/test-cases/llvm-integration/a15.c: New. - * unit-tests/test-cases/llvm-integration/b15.c: New. - * unit-tests/test-cases/llvm-integration/c15.c: New. - -2008-02-05 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: fix for -arch ppc -mdynamic-no-pic -pie so PPC_RELOC_HA16 reloc is used - ------ Tagged ld64-82.3 - -2008-02-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5721186> ld doesn't seem to understand $ld$add$os... and $ld$hide$os... for 10.6 moves - * src/ObjectFile.h: add 10.6 - * src/Options.cpp: add 10.6 support - * src/MachOReaderDylib.hpp: recognize $os10.6$ - - ------ Tagged ld64-82.2 - -2008-01-30 Devang Patel <dpatel@apple.com> - - <rdar://problem/5714833> Can't build 64-bit Intel binaries with LTO - <rdar://problem/5714787> ld64 fails to build with llvm-gcc-4.2 - * src/LLVMReader.hpp: Fix character count typo in strncmp call. - Use const char * to initialize temp. string. - * ld64.xcodeproj/project.pbxproj: use $(DEVELOPER_DIR) in header search construction - instead of hard coding /Developer. - ------ Tagged ld64-82.1 - -2008-01-23 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: don't bus error if S_LITERAL_POINTERS is missing relocs - - -2008-01-22 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5695496> ld uses 32-bits in some places to hold addresses when parsing 64-bit mach-o files - * src/MachOReaderRelocatable.hpp: use AddrToAtomMap type that switch address to 64-bits for 64-bit archs - * src/MachOWriterExecutable.hpp: verify BR14 does not overflow for external relocs - * unit-tests/test-cases/relocs-c: update test case to slide addresses to verify x86_64 .o files - - ------ Tagged ld64-82 - -2008-01-18 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5694612> Bad grammar used in ld warning: cannot exported hidden symbol - * src/ld.cpp: fix typo in warning string - - -2008-01-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5565074> Bundle Loader does not work anymore when loader is a bundle - <rdar://problem/5590203> ld warns of incorrect architecture when linking a bundle to a bundle - * src/MachOReaderDylib.hpp: support linking against bundles via -bundle_loader. Clean up error messages - * unit-tests/test-cases/bundle_loader: update test case - - -2008-01-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5366363> ld -r -x creates debug notes (stabs) when it should not with -x (keep only global symbols) - * src/Options.cpp: in reconfigureDefaults() if -r and -x then -S - - -2008-01-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5566068> if ld crashes while writing output file, it should delete the half written file - * src/MachOWriterExecutable.hpp: wrap open/write/close in try block and add signal handlers all to delete - output file on failure. - - -2008-01-16 Devang Patel <dpatel@apple.com> - - * src/LLVMReader.hpp: Use __gnu_cxx::hash_map instead of hash supported by LLVM. - - -2008-01-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5593537> GC-supported library can't be linked into GC-required executable - * src/ld.cpp: loosen constraint that all objc code must be compiled with same GC settings and - allow gc-compatible code to be linked into anything. - * unit-tests/test-cases/objc-gc-checks: update test case - - -2008-01-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5687976> no debug notes for custom named data - * src/ld.cpp: in synthesizeDebugNotes() check getSymbolTableInclusion() instead of for leading underscore - * unit-tests/test-cases/dwarf-debug-notes: update test case - ------ Tagged ld64-81.5 - -2008-01-14 Devang Patel <dpatel@apple.com> - - <rdar://problem/5683813> llvm-gcc-4.2 fails to build Sqlite 3.5.4 with -O4 - * src/LLVMReader.hpp: Resolve proxy references. Collect new unbounded references - after optimization. - * src/ld.cpp: Resolve additional unbounded references after optimization. - - -2008-01-14 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5655246> PPC Leopard (Xcode 3.0) linker ld gets "Bus error" sometimes - * src/MachOReaderRelocatable.hpp: use same code as x86 to parse ppc and arm sect-diff relocs - * src/MachOWriterExecutable.hpp: use same code as x86 to write ppc and arm sect-diff relocs - - -2008-01-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5637618> PPC Leopard (Xcode 3.0) linker ld reports "unknown scattered relocation type 4" - * src/MachOReaderRelocatable.hpp: add PPC_RELOC_HI16 to scattered reloc parsing - * unit-tests/test-cases/relocs-asm/relocs-asm.s: added tests for scattered hi/lo instructions - - -2008-01-11 Nick Kledzik <kledzik@apple.com> - - * doc/man/man1/ld.1: add doc for -no_implicit_dylibs, -read_only_stubs, -slow_stubs, -interposable_list - - -2008-01-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4800212> ld64(1) man page uses ambiguous term "suffix" - * doc/man/man1/ld.1: make meaning of "suffix" more explicit - - -2008-01-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5633081> Obj-C Symbols in Leopard Can't Be Weak Linked - * src/MachOWriterExecutable.hpp: set weak and lazy attributes on dummy .objc_class_name undefines - to dylibs to support Mac OS X 10.3.x dyld - - -2008-01-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5669751> Unknown error with linker (dyld: unknown external relocation type) - * src/ld.cpp: fix crash when SO stabs are not balanced - - -2008-01-11 Devang Patel <dpatel@apple.com> - - <rdar://problem/5667433> LTO does not work if expected output is a dynamic library - * src/LLVMReader.hpp: Supply arguments describing output kind to optimizer. Communicate - visibility info. - -2000-01-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5680988> __cls_refs section is losing S_LITERAL_POINTERS section type - * src/MachOWriterExecutable.hpp: special case __cls_refs section - * unit-tests/test-cases/objc-literal-pointers: add test case - - -2008-01-03 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5667688> wrong EH information might be used - Created new kGroupSubordinate reference kind to model group comdat. The "signature" atom - has kGroupSubordinate references to the other atoms in the group. If the signature atom - is coalesced away, the linker follows kGroupSubordinate references and throws away the - other members of the group. - * unit-tests/test-cases/eh-coalescing: added test case - * src/ld.cpp: added markDead() and use propagate to subordinates - * src/Architectures.hpp: added kGroupSubordinate - * src/MachOReaderRelocatable.hpp: add kGroupSubordinate reference from a function to its .eh atom - and if used, from .eh atom to its LSDA atom. - * src/MachOWriterExecutable.hpp: handle kGroupSubordinate like kNoFixUp - ------ Tagged ld64-81.4.1 - -2007-12-19 Devang Patel <dpatel@apple.com> - - * src/LLVMReader.hpp: Add LLVM_LTO_VERSION #ifdef check. - -2007-12-19 Devang Patel <dpatel@apple.com> - - * src/LLVMReader.hpp: Add fOptimizer NULL check before calling printVersion(). - -2007-12-19 Devang Patel <dpatel@apple.com> - - <rdar://problem/5655956> print LLVM LTO version number in verbose mode - * src/LLVMReader.hpp: Add printLLVMVersion() to print llvm version string in verbose mode. - * src/Options.cpp: Use printLLVMVersion() in verbose mode. - -2007-12-19 Devang Patel <dpatel@apple.com> - - <rdar://problem/5655956> print LLVM LTO version number in verbose mode - * src/Options.h: Add verbose() method to check fVerbose flag. - * src/LLVMReader.hpp: Print LLVM version string in verbose mode. - ------ Tagged ld64-81.4 - -2007-12-18 Devang Patel <dpatel@apple.com> - - * src/LLVMReader.hpp: Invalidate input architecture when optimizer is not available. - ------ Tagged ld64-81.3 - -2007-12-17 Nick Kledzik <kledzik@apple.com> - - * ld64.xcodeproj/project.pbxproj: remove extraneous header search paths - - -2007-12-17 Devang Patel <dpatel@apple.com> - - * src/LLVMReader.hpp: Do not throw exception if LLVMReader is not able to - dlopen LTO library. Instead just flag input file as an invalid LLVM bitcode file. - - -2007-12-14 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5645908> gcc DejaGnu failure: gcc.dg/20020312-2.c (test for excess errors) (-fstack-protector-all) - * src/MachOWriterExecutable.hpp: fix Writer<x86>::generatesExternalTextReloc() to allow text relocs - * unit-tests/test-cases/read-only-relocs: updated test case to link a dynamic main executable compiled with -static - - -2007-12-14 Devang Patel <dpatel@apple.com> - - <rdar://problem/5648438> Enable Link Time Optimization in Opal - * src/LLVMReader.hpp: Locate LLVMlto.dylib relative to ld location in Developer folder. - * ld64.xcodeproj/project.pbxproj: Add {DEVELOPER_DIR}/usr/include in header search path. - * unit-tests/run-all-unit-tests: Set DYLD_FALLBACK_LIBRARY_PATH to find LLVMlto.dylib during unit testing. - * unit-tests/testcases/llvm-integration/Makefile: Point LLVMGCC and LLVMGXX to llvm-gcc-4.2 in Developer folder during unit testing. - - -2007-12-13 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5645446> SWB: failures due to ld: pointer in read-only segment not allowed in slidable image, used in ... - * src/MachOReaderRelocatable.hpp: in Reader<x86>::addRelocReference() handle weak pc-rel 32-bit vanilla relocs properly - ------ Tagged ld64-81.2 - - - -2007-12-07 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5615298> support 8-bit relocations for i386 - * src/Architectures.hpp: add kPCRel8 - * src/MachOReaderRelocatable.hpp: support 8-bit pc-rel relocations for intel - * src/MachOWriterExecutable.hpp: support 8-bit pc-rel relocations for intel - * unit-tests/test-cases/relocs-asm: add test cases - - ------ Tagged ld64-81.1 - -2007-12-06 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderDylib.hpp: rework cycle detection to remove some false positives - - -2007-12-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5628149> Duplicate probe firings in Security.framework - * src/ld.cpp: check dtrace probe sites are not in fDeadAtoms before using - * unit-tests/test-cases/dtrace-static-probes-coalescing: add test case - - -2007-12-05 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fix CFString coalescing to work with -fwritable-strings - * unit-tests/test-cases/cfstring-coalesce: add -fwritable-strings to test case - - ------ Tagged ld64-81 - -2007-11-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4196067> ld64 should support runtime text relocations - * src/MachOWriterExecutable.hpp: add generatesLocalTextReloc() and generatesExternalTextReloc() - * src/Options.cpp: process -read_only_relocs option - * src/Options.h: add allowTextRelocs() and warnAboutTextRelocs() - * src/MachOReaderRelocatable.hpp: add hasLongBranchStubs() - * src/machochecker.cpp: allow relocs in read only segments, if section flags are set - * unit-tests/test-cases/read-only-relocs: update test case - - -2007-11-08 Devang Patel <dpatel@apple.com> - - * ld64.xcodeproj/project.pbxproj: add new build phase "build configure.h" for - ld target. - * src/ld.cpp: Include "configure.h" - - ------ Tagged ld64-80.11 - -2008-02-12 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5741312> Wrong section name for objc info for ARM when OBJC2 is used - * src/MachOWriterExecutable.hpp: switch segment/section name for ARM objc2 image info - ------ Tagged ld64-80.10 - -2008-02-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5733578> ld64 does not support -aspen_version_min 2.0 - * src/Options.cpp: allow 2.x for -aspen_version_min - - -2008-02-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5733575> ld_classic: unknown flag: -aspen_version_min - * src/Options.cpp: change -aspen_version_min x.x to -macosx_version_min 10.5 when invoking ld_classic - - ------ Tagged ld64-80.9 - -2008-01-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5713054> -iphone_version_min ==> -aspen_version_min - * src/Options.cpp: support -aspen_version_min - - ------ Tagged ld64-80.8 - -2008-01-10 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: support transition to new objc ABI for ARM by allowing old .objc_class_name_* - style names in export files and map them to new _OBJC_CLASS_$_ style names. - - ------ Tagged ld64-80.7 - -2008-01-02 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5572168> BigBear5A18 isn't fully prebound - * src/Options.cpp: make fNeedsModuleTable true for arm - ------ Tagged ld64-80.6 - -2007-11-30 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5620976> -iphone_version_min - * src/Options.cpp: handle -iphone_version_min option - - ------ Tagged ld64-80.5 - -2007-11-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5601142> need to special case some dylibs in seg_addr_table - * src/Options.cpp: retry seg_add_table lookup for a couple of unusual dylibs - - ------ Tagged ld64-80.4 - -2007-11-06 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fix parsing of external and scattered thumb branch22 relocs - * unit-tests/test-cases/thumb-blx: add test case to keep blx issues from coming back - ------ Tagged ld64-80.3 - -2007-11-03 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: remove recalc of dstAddr which could cause thumb branches to be +2 - * src/MachOWriterExecutable.hpp: remove incorrect test for relocateableExternal - ------ Tagged ld64-80.2 - -2007-11-01 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: hack my own prototype for log2() until math.h is cleaned up - - ------ Tagged ld64-80.1 - -2007-11-01 Nick Kledzik <kledzik@apple.com> - - * ld64.xcodeproj/project.pbxproj: add HEADER_SEARCH_PATHS for cross builds - * src/ld.cpp: temporarily disable LLVM_SUPPORT - * src/MachOWriterExecutable.hpp: Don't use CC_MD5() directly - - -2007-10-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5556038> Cannot build with libm_static.a statically linked - * src/MachOWriterExecutable.hpp: Fix makesExternalRelocatableReference() for -r -d case - * unit-tests/test-cases/tentative-to-real-hidden: add test case - - ------ Tagged ld64-80 - -2007-10-24 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4222696> linker should probably warn about trying to export a hidden symbol - * src/ld.cpp: if using -exported_symbols_list check each hidden atom as it is added to symbol table - * src/Options.h,.cpp: add hasExportMaskList() - * unit-tests/test-cases/exported_symbols_list-hidden: added test case - - -2007-10-24 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: keep old style dtrace probes externel for kernel builds - - -2007-10-23 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4556199> unify error and warning messages - <rdar://problem/5546450> -w should suppress warnings - * src/ld.cpp: use warning() function - * src/Options.h: remove emitWarnings() - * src/MachOReaderDylib.hpp: use warning() function - * src/MachOReaderRelocatable.hpp: use warning() function - * src/Options.cpp: use and implement warning() - * src/MachOWriterExecutable.hpp: use warning() function - * unit-tests/test-cases/visibility-warning: verify -w suppresses warnings - - -2007-10-23 Devang Patel <dpatel@apple.com> - - * src/ld.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/LLVMReader.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/MachOReaderDylib.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/ObjectFile.h: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/MachOReaderRelocatable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/OpaqueSection.hpp: Cover arm support inside __OPEN_SOURCE__ macro check - * src/MachOWriterExecutable.hpp: Cover arm support inside __OPEN_SOURCE__ macro check. - * src/ObjectDump.cpp: Cover arm support inside __OPEN_SOURCE__ macro check. - - -2007-10-22 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: add support for LD_DEAD_STRIP and LD_WARN_COMMONS - * src/MachOReaderRelocatable.hpp: fix problem with -dead_strip of ObjC literal pointers - - -2007-10-22 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: have -static arm code link with ld_classic (for now) - - -2007-10-22 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5396826> Recognize all arm architectures - * src/MachOReaderRelocatable.hpp: add support for all ARM sub-types - * unit-tests/test-cases/cpu-sub-types: add test cases for all combinations of ARM sub-types - - -2007-10-19 Nick Kledzik <kledzik@apple.com> - - * src/*: merge in arm support - * unit-tests/test-cases/*: fix to work for arm and thumb - ------ Tagged ld64-79 - -2007-10-16 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: if -r mode, always set custom alignment (SET_COMM_ALIGN) on common symbols - * unit-tests/test-cases/visibility-warning-dylib-v-archive/Makefile: fix warning - * unit-tests/test-cases/static-executable/Makefile: fix spurious failure - - -2007-10-16 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix edge case in branch island generation - - -2007-10-12 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5323449> Add option to create old, slow stubs for i386 - * src/ObjectFile.h/.cpp: support -read_only_stubs - * src/MachOWriterExecutable.hpp: enhance StubAtom<x86> to support old style __symbol_stub/__la_symbol_ptr stubs - * unit-tests/test-cases/slow-x86-stubs: add test case - - -2007-10-12 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5427952> ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard - * src/Options.cpp: in findFileUsingPaths() don't search for embedded dylibs - * unit-tests/test-cases/indirect-path-search/Makefile: added case for a dylib embedded in a framework - - -2007-10-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5451987> add option to disable implicit load commands for indirectly used public dylibs - * src/Options.cpp: add support for -no_implicit_dylibs - * src/ObjectFile.h: add fImplicitlyLinkPublicDylibs - * src/MachOReaderDylib.hpp: test fImplicitlyLinkPublicDylibs before hoisting an implicitly linked dylib - * unit-tests/test-cases/implicit_dylib: add test case - - -2007-10-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5254413> -interposable_list - * src/Options.h/cpp: Add fInterposeList and fInterposeMode to support -interposable_list - * src/MachOWriterExecutable.hpp: pass symbol name to fOptions.interposable() - * unit-tests/test-cases/interposable_list: add test case - - -2007-10-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - * src/MachOWriterExecutable.hpp: automatically use LC_LOAD_WEAK_DYLIB if all symbols used from a dylib are weak_import - * unit-tests/test-cases/weak_dylib: added test case - - -2007-10-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5504954> linker does not error when dylib ordinal exceeds 250 - * src/MachOWriterExecutable.hpp: error out if ordinals exceed max allowed - - -2007-10-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4067110> overriding 'operator new' or 'operator delete' fails if no weak symbols are present - * src/ld.cpp: at end of checkUndefines() search dylibs for weak versions of any global external symbols - * src/ObjectFile.h: add hasWeakExternals() method to Reader - * src/MachOReaderDylib.hpp: implement hasWeakExternals() method in Reader - * src/ExecutableFile.h: add overridesDylibWeakDefines parameter to write() - * src/MachOWriterExecutable.hpp: use overridesDylibWeakDefines parameter to write() - * unit-tests/test-cases/operator-new: add test case - - -2007-10-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5048861> No warning about tentative definition conflicting with dylib definition - <rdar://problem/5132652> .comm variables in shared library, worked with XCode 2.4.1, broken with XCode 3? - * src/ld.cpp: at end of checkUndefines() verify if any remaining commons conflict with dylibs - * doc/man/man1/ld.1: document -commons and -warn_commons options - * unit-tests/test-cases/tentative-and-dylib: added test case - - -2007-10-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5346331> NS/CFString constants are not dead strippable - * src/MachOReaderRelocatable.hpp: break up __cfstring section into one atom per cfstring, make them coalesable - * unit-tests/test-cases/cfstring-coalesce: added test case - - -2007-10-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5524973> Dead stripping + exported symbols list using wildcards doesn't seem to do the right thing - * src/Options.cpp/h: add hasWildCardExportRestrictList() - * src/ld.cpp: if dead stripping code and have wildcard exports, add all global atoms matching wildcards as roots - * unit-tests/test-cases/exported-symbols-wildcards-dead_strip: added test case - - -2007-10-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5433882> ld shouldn't search /Network/Library/Frameworks by default - * src/Options.cpp: remove /Network/Library/Frameworks/ from default search path - * doc/man/man1/ld.1: document the change - - -2007-10-04 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5341567> all binaries should get LD_UUID load commands, not just those with DWARF symbols - * src/ld.cpp: default fCreateUUID to be true for non object file output types - * unit-tests/test-cases/no-uuid/Makefile: update test case to match new rules - - ------ Tagged ld64-78 - -2007-09-27 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5476313> range check load commands - * src/MachOReaderDylib.hpp: check that load commands all fit in load command size from header - * src/MachOReaderRelocatable.hpp: check that load commands all fit in load command size from header - - -2007-09-27 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5433355> Xc8M2540a: ld64 crashes when linking Pascal program - * src/ld.cpp: fix findAtomAndOffset() to handle where there are no function atoms - - -2007-09-27 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5241179> ADOBE Xcode 3: ld -dead_strip does not work with -init from an archive - * src/ld.cpp: add bool parameter to entryPoint() so -init atom not looked for too soon - * unit-tests/test-cases/dead_strip-init-archive: added test case - - -2007-09-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5459546> Spurious link warnings for inline members of C++ template classes - * src/ld.cpp: check definition kinds before warning about visibility mismatches - * unit-tests/test-cases/visibility-warning: added test case - - -2007-09-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5394172> an empty .o file with zero load commands will crash linker - * src/MachOReaderRelocatable.hpp: have Reader constructor return early of no load commands - * unit-tests/test-cases/empty-object: added test case - - -2007-09-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5453384> 9a527: ppc64 branch islands fail with 4GB pagezeo - * src/MachOWriterExecutable.hpp: start range calculations at start of __text not at zero. - - ------ Tagged ld64-77 (Xcode 3.0) - -2007-07-23 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5351380> 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 <kledzik@apple.com> - - <rdar://problem/5254468> 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 <kledzik@apple.com> - - <rdar://problem/5349847> 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 <kledzik@apple.com> - - <rdar://problem/5303718> 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 <kledzik@apple.com> - - <rdar://problem/5299907> 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 <kledzik@apple.com> - - <rdar://problem/5277857> 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 <kledzik@apple.com> - - <rdar://problem/5286462> 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 <kledzik@apple.com> - - <rdar://problem/5297034> -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 <kledzik@apple.com> - - <rdar://problem/4135857> 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 <kledzik@apple.com> - - <rdar://problem/5285473> 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 <kledzik@apple.com> - - <rdar://problem/4607755> 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 <kledzik@apple.com> - - <rdar://problem/5241902> 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 <kledzik@apple.com> - - <rdar://problem/5224676> 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 <kledzik@apple.com> - - <rdar://problem/5211667> 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 <kledzik@apple.com> - - <rdar://problem/5201463> 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 <kledzik@apple.com> - - <rdar://problem/5008421> 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 <kledzik@apple.com> - - <rdar://problem/5198807> ld64 drops fix&continue bit in __OBJC, __image_info. - * src/MachOReaderRelocatable.hpp: implement objcReplacementClasses() - - -2007-05-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5066152> 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 <kledzik@apple.com> - - * unit-tests/include/common.makefile: set COMPILER_PATH so harness works with latest compiler - - ------ Tagged ld64-74.2 - -2007-05-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5195447> 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 <kledzik@apple.com> - - * src/Options.h: add emitWarnings() - * src/Options.cpp: wire up -w to emitWarnings() - - -2007-05-09 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5137285> 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 <kledzik@apple.com> - - <rdar://problem/5182537> 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 <kledzik@apple.com> - - <rdar://problem/5181105> typo in error message for linking -pie - * src/MachOWriterExecutable.hpp: fix typo in error messages - - ------ Tagged ld64-74 - -2007-05-03 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5171880> ld64 can't find @executable _path relative dylibs from our umbrella frameworks - <rdar://problem/4019497> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/5177848> typo in message, frameowrk - * src/Options.cpp: fix typo - - -2007-05-01 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4977301> "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 <kledzik@apple.com> - - <rdar://problem/5113424> ld doesn't handle -mlong-branch .o files that have had local symbols stripped - <rdar://problem/4965359> 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 <kledzik@apple.com> - - <rdar://problem/5065659> 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 <kledzik@apple.com> - - <rdar://problem/5062685> ld does not report error when -r is used and exported symbols are not defined. - <rdar://problem/4637139> 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 <kledzik@apple.com> - - <rdar://problem/5137732> 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 <kledzik@apple.com> - - <rdar://problem/5166572> 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 <kledzik@apple.com> - - <rdar://problem/5109373> 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 <kledzik@apple.com> - - <rdar://problem/5008421> 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 <kledzik@apple.com> - - <rdar://problem/5150407> 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 <kledzik@apple.com> - - <rdar://problem/5055233> 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 <kledzik@apple.com> - - <rdar://problem/5066152> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/5194804> 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 <kledzik@apple.com> - - <rdar://problem/5191610> 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 <kledzik@apple.com> - - <rdar://problem/5140897> 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 <kledzik@apple.com> - - <rdar://problem/5129379> 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 <kledzik@apple.com> - - <rdar://problem/5067034> 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 <kledzik@apple.com> - - <rdar://problem/5071047> 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 <kledzik@apple.com> - - <rdar://problem/5125280> 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 <kledzik@apple.com> - - <rdar://problem/5130496> -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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/5105971> 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 <kledzik@apple.com> - - <rdar://problem/5102873> 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 <kledzik@apple.com> - - <rdar://problem/5102845> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4175790> ER: -dead_strip_dylibs - <rdar://problem/3904828> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/5073800> 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 <kledzik@apple.com> - - <rdar://problem/4739044> 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<ppc>. 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 <kledzik@apple.com> - - <rdar://problem/5085863> Typo in ld man page (-exported_symbols_list) - * doc/man/man1/ld.1: fix typo - - -2007-03-26 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4727750> 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 <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: restructure writeAtoms() to copy all atoms in memory if possible - - -2007-03-24 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5082603> 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 <kledzik@apple.com> - - <rdar://problem/5084564> 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 <kledzik@apple.com> - - * src/ld.cpp: fix -print_statistics when using -dead_strip - - -2007-03-23 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: generate better names for non-lazy pointers to the interior of atoms - - -2007-03-16 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: speed up ld -r a little by reversing relocs en mas - - -2007-03-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4975277> ld Bus Error on missing command line arguments - * src/Options.cpp: check next argv[] is not NULL - - -2007-03-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4832049> 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 <kledzik@apple.com> - - <rdar://problem/5042552> 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 <kledzik@apple.com> - - <rdar://problem/4592484> Linker returns success although exported symbols are undefined. - * src/ld.cpp: turn missing symbols back into an error - - -2007-03-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4019497> 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 <kledzik@apple.com> - - <rdar://problem/3904828> linker should add implicit load commands for indirectly used public dylibs - <rdar://problem/4142277> Indirect libraries should be found using -F and -L options - <rdar://problem/4607755> 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 <kledzik@apple.com> - - <rdar://problem/4982400> 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 <kledzik@apple.com> - - * src/Options.cpp: ignore .objc_category_name_* symbols in .exp files - - -2007-03-06 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: stop special casing mach_kernel and instead requre kernel to be built with -new_linker - - -2007-03-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/5044253> 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 <kledzik@apple.com> - - <rdar://problem/5026135> 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 <kledzik@apple.com> - - <rdar://problem/5033206> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * doc/man/man1/ld.1: Add descriptions to all "rarely used options" - - -2007-03-01 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4971033> 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 <kledzik@apple.com> - - <rdar://problem/5040314> 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 <kledzik@apple.com> - - <rdar://problem/4739044> 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 <kledzik@apple.com> - - <rdar://problem/3965017> 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 <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: rework how padding after load commands is calculated - - -2007-02-21 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: extend special case of __mh_execute_header to static executables too - - -2007-02-21 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/3882294> 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 <kledzik@apple.com> - - <rdar://problem/4557734> 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 <kledzik@apple.com> - - <rdar://problem/4995303> 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 <kledzik@apple.com> - - <rdar://problem/4988068> 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 <kledzik@apple.com> - - * src/ld.cpp: sort __OBJC2 segment to be next to __OBJC segment - - -2007-02-07 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: change missing -seg_addr_table from an error to a warning - - -2007-02-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4977311> 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 <kledzik@apple.com> - - * src/MachOReaderDylib.hpp: don't warn about zero size __image_info sections - - -2007-02-04 Rick Balocca <rbalocca@apple.com> - Enable the failing cases for missing command line arguments - -2007-02-04 Rick Balocca <rbalocca@apple.com> - Make sure that all .o's are checked by ObjectDump - and all macho are checked by machochecker - -2007-02-04 Rick Balocca <rbalocca@apple.com> - Fix an endian problem with machochecker - Fix blank-stubs Makefile - ------ Tagged ld64-71 - -2007-02-02 Rick Balocca <rbalocca@apple.com> - 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 <rbalocca@apple.com> - make cpu-sub-types test more robust - -2007-02-01 Rick Balocca <rbalocca@apple.com> - auto-arch tests were resulting in a false FAILs - -2007-02-01 Rick Balocca <rbalocca@apple.com> - test cpu-sub-types was resulting in a false FAIL - -2007-02-01 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4969335> 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 <kledzik@apple.com> - - <rdar://problem/4965743> 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 <kledzik@apple.com> - - <rdar://problem/4967535> 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 <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: in addObjectRelocs_powerpc() mask scattered r_address to 16-bits - ------ Tagged ld64-70 - - -2007-01-30 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4810668> linker should verify GC consistency of modules being linked into library - <rdar://problem/4474195> 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 <kledzik@apple.com> - - src/*: remove ObjectFile::requiresFollowOnAtom() method - - -2007-01-28 Nick Kledzik <kledzik@apple.com> - - 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 <rbalocca@apple.com> - * src/ObjectDump.cpp: The usage() message was incorrect. - - -2007-01-25 Rick Balocca <rbalocca@apple.com> - * 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 <kledzik@apple.com> - - <rdar://problem/4952297> 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 <kledzik@apple.com> - - * 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 <echristo@apple.com> - - <rdar://problem/4856341> ld64 --help isn't recognized - * src/Options.cpp (Options::parse): Support --help and -help. - - -2007-01-15 Nick Kledzik <kledzik@apple.com> - - * src/MachOFileAbstraction.hpp: add range checking on macho_scattered_relocation_info::set_r_address() - - -2007-01-14 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4514409> 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 <kledzik@apple.com> - - <rdar://problem/4868270> [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 <kledzik@apple.com> - - <rdar://problem/4964508> 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 <kledzik@apple.com> - - <rdar://problem/4949507> 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 <kledzik@apple.com> - - <rdar://problem/4947347> 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 <kledzik@apple.com> - - <rdar://problem/4946075> 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 <kledzik@apple.com> - - <rdar://problem/4925645> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4889729> -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 <kledzik@apple.com> - - <rdar://problem/4889409> 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 <kledzik@apple.com> - - <rdar://problem/4886721> -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 <kledzik@apple.com> - - <rdar://problem/4886652> -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 <kledzik@apple.com> - - <rdar://problem/4879913> 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 <kledzik@apple.com> - - <rdar://problem/4868738> 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 <kledzik@apple.com> - - <rdar://problem/4874209> symbols with REFERENCED_DYNAMICALLY should never be stripped - * src/MachOWriterExecutable.hpp: update Writer<A>::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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4858299> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4855542> 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 <kledzik@apple.com> - - <rdar://problem/4433496> 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 <kledzik@apple.com> - - <rdar://problem/4795615> -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 <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: support __IMPORT segment being slide independently of __DATA segment in shared cache - - -2006-11-16 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4838262> 9a303: ld -filelist Bus Error - * src/Options.cpp: add check that -filelist is followed by an argument - - -2006-11-16 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: when building split-seg dylibs, LINKEDIT goes in read-only side - - -2006-11-15 Nick Kledzik <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: redirect references to static weak stubs to the real target - - -2006-11-09 Nick Kledzik <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: initialize fModuleInfoAtom to zero - - -2006-11-08 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4821985> 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 <kledzik@apple.com> - - <rdar://problem/4824368> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4814565> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4729162> 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 <kledzik@apple.com> - - <rdar://problem/4474316> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4585230> 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 <dpatel@apple.com> - - * src/LLVMReader.hpp: Supply final output file path to optimizer. - -2006-10-26 Devang Patel <dpatel@apple.com> - - * src/ObjectFile.h: Make setSection* methods virtual. - * src/LLVMReader.hpp: Override setSection* methods. - -2006-10-26 Devang Patel <dpatel@apple.com> - - * 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 <dpatel@apple.com> - - * src/options.h, src/options.cpp: Add -save-temps command line option. - * src/LLVMReader.hpp: Use saveTemps option. - - -2006-10-26 Devang Patel <dpatel@apple.com> - - * src/LLVMReader.hpp: Remove invalid module from memory. - -2006-10-26 Devang Patel <dpatel@apple.com> - - * src/LLVMReader.hpp: Collect symbol alignment info from LLVM optimizer. - -2006-10-21 Eric Christopher <echristo@apple.com> - - * 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 <kledzik@apple.com> - - * ld64.xcodeproj/project.pbxproj: stop copying LLVMReader.hpp into man1 directory - ------ Tagged ld64-64.1 - -2006-10-19 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4791643> 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 <echristo@apple.com> - - * 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 <kledzik@apple.com> - - <rdar://problem/4783853> ld64 change required to go with assembler cstring change - <rdar://problem/4732996> 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 <kledzik@apple.com> - - <rdar://problem/4786250> 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 <kledzik@apple.com> - - <rdar://problem/4756806> 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 <kledzik@apple.com> - - <rdar://problem/4769120> 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 <kledzik@apple.com> - - * 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 <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: rework dwarf line parsing to fix warnings that starting - showing up with gcc-5421 - - -2006-10-05 Eric Christopher <echristo@apple.com> - - <rdar://problem/4760935> 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 <echristo@apple.com> - - * src/Options.cpp (Options::gotoClassicLinker): Use execvp - to call ld_classic. - -2006-10-03 Eric Christopher <echristo@apple.com> - - * unit-tests/test-cases/tentative-to-real/Makefile: Clean up after tests. - -2006-10-03 Eric Christopher <echristo@apple.com> - - * unit-tests/include/common.makefile (VALID_ARCHS): Add x86_64. - (OTOOL): Remove munging based on ARCH. - -2006-09-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4743925> 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 <kledzik@apple.com> - - src/Options.h/.cpp: add support for -segaddr option - src/MachOWriterExecutable.hpp: In Writer::assignFileOffsets(), use -segaddr info - - -2006-09-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4587349> 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 <dpatel@apple.com> - - 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 <kledzik@apple.com> - - ld64.xcodeproj/project.pbxproj: remove accidental install of source file into man1 - - -2006-09-25 Nick Kledzik <kledzik@apple.com> - - 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 <kledzik@apple.com> - - src/MachOWriterExecutable.hpp: include stubs in LC_SEGMENT_SPLIT_INFO - - -2006-09-21 Nick Kledzik <kledzik@apple.com> - - src/Options.cpp: disable split-seg dylibs for 64-bit architectures - - -2006-09-19 Nick Kledzik <kledzik@apple.com> - - 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 <kledzik@apple.com> - - src/MachOWriterExecutable.hpp: rework encoding of LC_SEGMENT_SPLIT_INFO - - -2006-09-19 Nick Kledzik <kledzik@apple.com> - - src/Options.cpp: check for -search_paths_first in first pass - - ------ Tagged ld64-63 - -2006-09-15 Nick Kledzik <kledzik@apple.com> - - 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 <kledzik@apple.com> - - src/Options.cpp: support -r -static - src/MachOWriterExecutable.hpp: support -r -static an don't generate LC_DYSYMTAB - - -2006-09-14 Nick Kledzik <kledzik@apple.com> - - 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 <kledzik@apple.com> - - <rdar://problem/4718189> 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 <kledzik@apple.com> - - <rdar://problem/4474172> 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 <kledzik@apple.com> - - <rdar://problem/4464904> 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 <kledzik@apple.com> - - <rdar://problem/4070025> 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 <kledzik@apple.com> - - <rdar://problem/3894079> 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 <kledzik@apple.com> - - <rdar://problem/4637023> need -i for 64-bit (or equivalent) - <rdar://problem/4014529> 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 <kledzik@apple.com> - - <rdar://problem/4458878> 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 <kledzik@apple.com> - - 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 <kledzik@apple.com> - - <rdar://problem/4571042> Add convention for removing symbols at link time - <rdar://problem/3962731> 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 <kledzik@apple.com> - - 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 <kledzik@apple.com> - - <rdar://problem/4701529> generate LC_SEGMENT_SPLIT_INFO for 10.5 or later dylibs - src/Options.h&.cpp: implement sharedRegionEligible() to control when LC_SEGMENT_SPLIT_INFO is added - src/MachOFileAbstraction.hpp: add macho_linkedit_data_command - src/MachOWriterExecutable.hpp: generate LC_SEGMENT_SPLIT_INFO load command and linkedit content - ------ Tagged ld64-62 - -2006-08-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4681062> wrong error message when symbol is found in unused indirect library - src/ld.cpp: remove indirect libraries if they are not re-exported - unit-tests/test-cases/indirect-dylib: added test case - - -2006-08-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/3930461> alignment needs to be richer - src/ObjectFile.h: define ObjectFile::Alignment class for tracking rich alignment info - src/ld.cpp: modify SymbolTable::add() to work with new Alignment type - src/MachOReaderRelocatable.hpp: use new Alignment type. Remove alignAtLeast() and handleAnonymousNonLazyPointers() - src/MachOWriterExecutable.hpp: update for new Alignment type, use modulus when calculating layout address - src/ObjectDump.cpp: print richer Alignment info - unit-tests/test-cases/align-modulus: added test case - - -2006-08-11 Nick Kledzik <kledzik@apple.com> - - remove OPEN_SOURCE conditionals around x86_64 support - - -2006-07-31 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4654131> 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 <kledzik@apple.com> - - <rdar://problem/4656617> x86_64: instructions with immediate and rip-relative operands need to use new relocation types - src/MachOWriterExecutable.hpp: generate new reloc types in -r mode - src/MachOReaderRelocatable.hpp: parse new reloc types - unit-tests/test-cases/relocs-asm/relocs-asm.s: add test cases for new reloc type - - -2006-07-18 Nick Kledzik <kledzik@apple.com> - - 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 <kledzik@apple.com> - - <rdar://problem/4634840> faster debug note generation - src/ld.cpp: rework collectDebugInfo() to produce all debug notes in one pass, intead of a - pass per .o file. Added timing info for collectDebugInfo() to -print_statistics - unit-tests/test-cases/dwarf-debug-notes-r/Makefile: add expliced -arch to ld -r - unit-tests/test-cases/dwarf-debug-notes-r/expected-stabs: alter for new debug notes order - - -2006-07-17 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4623994> ld64 VSIZE is 1.18GB when building Finder ppc64 - src/ld.cpp: fixed typo in createReader() that prevented dylibs from being unmapped - ------ Tagged ld64-61.1 - -2006-07-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4622049> ld64-61: gcc DejaGnu tests failing due to -arch followed by unknown architecture name - src/Options.cpp: map ppc750, ppc7400, ppc7450, and ppc970 to ppc. Improve error message - -2006-07-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4622769> If -arch is missing, rollover to ld_classic does not happen - src/Options.h: make gotoClassicLinker() public - src/ld.cpp: call gotoClassicLinker() if the inferred architecture is ppc or i386 - ------ Tagged ld64-61 - -2006-06-29 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4606628> ld64 should be renamed to ld - src/Options.cpp: exec() ld_classic if -arch ppc or -arch i386 is seen - src/ld.cpp: alter version string - ld64.xcodeproj/project.pbxproj: change install location to /usr/bin/ld, add symlink from /usr/bin/ld64 - doc/man/man1/ld.1: added - ------ Tagged ld64-60 - -2006-06-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4604539> Can't link large ppc64 program: ld64 says "bl out of range" - MachOWriterExecutable.hpp: fix branch island generation to work for weak_import functions - and properly chain together branch islands - MachOReaderRelocatable.hpp: improve performance of huge .o file reading by sorted references - only when done - -2006-06-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4603454> MySQL-36 fails to build with ld64-59 - src/MachOReaderRelocatable.hpp: back out fix for 4585335 - src/MachOWriterExecutable.hpp: back out fix for 4585335 - -2006-06-27 Nick Kledzik <kledzik@apple.com> - - src/MachOReaderRelocatable.hpp: handle N_GSYM without ending :G() since that is how - dwarf debug notes are formed. - -2006-06-23 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4599239 objc class with no superclass causes bad undefined symbol - src/MachOReaderRelocatable.hpp: handle NULL superclass in objc_class - unit-tests/test-cases/relocs-objc/test.m: add case with no super class - - -2006-06-23 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4313369> ld64 doesn't support variant linking -framework fw,_debug - src/Options.cpp: enhance findFramework() to support suffixes - ------ Tagged ld64-59 - -2006-06-22 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4596726> 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 <kledzik@apple.com> - - <rdar://problem/4567995> 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 <kledzik@apple.com> - - <rdar://problem/4535036> ld64 seems to offset things incorrectly when using -r - src/MachOWriterExecutable.hpp: in -r mode, virtual sections should not increment address - - ------ Tagged ld64-58 - -2006-06-16 Nick Kledzik <kledzik@apple.com> - - src/rebase.cpp: fix page alignment problem - src/rebase.cpp: fix endianess problem with local non-lazy pointers - -2006-06-15 Nick Kledzik <kledzik@apple.com> - - src/rebase.cpp: fix to build in CurryWeed - ld64.xcodeproj/project.pbxproj: fix to build properly in CurryWeed - -2006-06-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4495309> Support .objc_class_name_* symbols - src/ObjectFile.h: Add kSymbolTableInAsAbsolute - src/MachOReaderRelocatable.hpp: synthesize references to required objc classes - src/MachOWriterExecutable.hpp: write objc_class_name as absolute symbol - unit-tests/test-cases/objc-references: added - -2006-06-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4484369> SECTION_ATTRIBUTES unset in ppc64 mach-o header - src/MachOWriterExecutable.hpp: add section attribute for sections with code - -2006-06-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4569407> ld64 bogus duplicate symbol name linking GNU libobjc - src/MachOReaderRelocatable.hpp: only special case Apple objc runtime objc classes - -2006-06-15 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4582999> x86_64: ".align" directive not honored - src/MachOReaderRelocatable.hpp: change code alignment to not depend on atom size - -2006-06-14 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4585335> jump table into middle of weak symbol causes error - src/MachOReaderRelocatable.hpp: create direct references to the interior of weak symbols - src/MachOWriterExecutable.hpp: do not error on absolute references to interior of weak symbols - -2006-06-13 Nick Kledzik <kledzik@apple.com> - - src/Options.cpp: allow -image_base as an alias for -seg1addr - -2006-06-13 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4585115> implement -d - src/Options.h: add fMakeTentativeDefinitionsReal - src/Options.cpp: set fMakeTentativeDefinitionsReal if -d option is found - src/MachOWriterExecutable.hpp: turn tentative into real definition if makeTentativeDefinitionsReal - unit-tests/test-cases/btentative-to-real: added test case - -2006-06-13 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4584355> implement -bundle_loader - src/Options.h: add fBundleLoader bit to DynamicLibraryOptions - src/Options.cpp: handle -bundle_loader - src/ld.cpp: pass fBundleLoader bit to MachOReaderDylib - src/MachOReaderDylib.hpp: support reading MH_EXECUTE files if fBundleLoader is set - src/MachOWriterExecutable.hpp: set bundle loader ordinal as EXECUTABLE_ORDINAL - unit-tests/test-cases/bundle_loader: added test case - -2006-06-12 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4583347> -syslibroot can cause "can't find ordinal for imported" error - src/MachOReaderDylib.hpp: in Reader::reExports() compare install path in addition to load path - - -2006-06-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4548935> Need rebasing tool - src/rebase.cpp: added - unit-tests/test-cases/rebase-basic: added - doc/man/man1/rebase.1: added - ld64.xcodeproj/project.pbxproj: added rebase target. changed all targets to build with dwarf - - -2006-06-10 Nick Kledzik <kledzik@apple.com> - - src/machochecker.cpp: add some ppc reloc sanity checking - ------ Tagged ld64-57 - -2006-06-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4565088> 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 <kledzik@apple.com> - - <rdar://problem/4572702> -sectcreate of a 0-byte section fails - MachOWriterExecutable.cpp: Don't error out on zero length segments - MachOWriterExecutable.cpp: For ppc64 reloc base address is the first writable segment iff - there is a writable segment >4GB from base address - -2006-06-04 Eric Christopher <echristo@apple.com> - - Radar 4560240 - Radar 3964999 - * src/ld.cpp (createReader): Fixed error message. - (resolve): Ditto. - (resolveFrom): Ditto. - (checkUndefines): Ditto. - ------ Tagged ld64-56 - -2006-05-23 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4558079> No debug notes for ObjC methods when linking with ld64 - ld.cpp: don't limit debug notes to functions starting with underscore - -2006-05-22 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4556982> ld64 spends much time in mach_o::relocatable::Reader<x86_64>::findAtomByName - * src/MachOReaderRelocatable.hpp: add makeReferenceToSymbol() so that x86_64 does not need to do by-name lookups - -2006-05-22 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4535044> remove inferring warning - * ld.cpp: Remove "inferring" warning. If a link failed and now arch was specifed add which arch was - inferred to error message - -2006-05-19 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4544001> ld64 does not honor -arch_multiple - * ld.cpp: If fOptions.printArchPrefix(), add architecture name to error message - -2006-05-19 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4555973> Support S_16BYTE_LITERALS section types - * src/MachOReaderRelocatable.hpp: support S_16BYTE_LITERALS - * src/MachOWriterExecutable.hpp: support S_16BYTE_LITERALS - -2006-05-19 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4548803> "warning can't parse dwarf compilation unit info" warnings building debug - * src/MachOReaderRelocatable.hpp: fix bugs in dwarf line table parsing - ------ Tagged ld64-55 - -2006-05-18 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4534339> Default the pagezero size to 4GB for x86-64 - * src/Options.cpp: Chnage default the pagezero size to 4GB for x86-64 - -2006-05-18 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4552825> x86_64 CarbonCore fails to link with "atom not found in symbolIndex" - * src/MachOWriterExecutable.hpp: in buildObjectFileFixups() don't call addObjectRelocs() on kNoFixUp references - -2006-05-18 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4553555> ld64: .section defaults to read-only - * src/MachOReaderRelocatable.hpp: default unknown segments to r/w - -2006-05-18 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4551990> -fvisibility=hidden causes crashes for x86_64 - * src/MachOWriterExecutable.hpp: properly handle RIP relative tentative definitions - -2006-05-12 Nick Kledzik <kledzik@apple.com> - - * src/Architectures.hpp: add x86::kAbsolute32 - * src/MachOReaderRelocatable.hpp: generate x86::kAbsolute32 for mdynamic-no-pic instructions - * src/MachOWriterExecutable.hpp: process x86::kAbsolute32 reference kind - ------ Tagged ld64-54 - -2006-05-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4545108> CF-393 failes to link for x86_64 - * src/MachOWriterExecutable.cpp: fix sign extension for Rel32 relocs in Writer<x86_64>::fixUpReferenceRelocatable - -2006-05-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4501434> warning arch x86_64 not found using i386 - * src/ld.cpp: remove hack to allow x86_64 to link against i386 dylibs - - -2006-05-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4543754> x86_64: .objc_class_name symbol names scrambled - * src/MachOReaderRelocatable.hpp: properly compute alignment of __OBJC __class sections - - -2006-05-08 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/3894083> Support -dead_strip - * src/Options.h/cpp: implement -why_load and -why_live. Enable -dead_strip. - * src/MachOReaderArchive.hpp: implement -why_load - * src/MachOReaderRelocatable.hpp: suppress GCC_except_table* symbols in final output - * src/ld.cpp: implement dead code stripping - * unit-tests/test-cases/dead_strip: added - ------ Tagged ld64-53 - -2006-05-05 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: make 10.4 be minimum OS version for newer architectures - -2006-05-05 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4147604> N_SO symbols in 64-bit builds have a zero address for n.n_value - * src/ld.cpp: for SO stabs, associate first and last atom in the SO range - * src/MachOWriterExecutable.hpp: use atom associated with SO stab to set ins n_value - -2006-05-05 Nick Kledzik <kledzik@apple.com> - - * MachOWriterExecutable.hpp: fix end FUN stab to have length of function - - -2006-05-02 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4496250> 64-bit main executables should have 4GB zero page by default - * src/Opptions.cpp: change default pagezero_size to 4GB for ppc64 - <rdar://problem/4492850> 64 bit: apps with -mdynamic-no-pic seg fault when page zero > 4GB - * src/MachOWriterExecutable.cpp: rework pagezero for ppc64 so that if any mdynamic-no-pic code - is found, the code is kept in the low 2GB, and a new segment is create to map away up to 4GB. - -2006-05-02 Nick Kledzik <kledzik@apple.com> - - * src/Opptions.cpp: remove warning about -stack_addr not specified. Add warning if 32-bit stack - overlaps shared region - ------ Tagged ld64-52.1 - -2006-05-01 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.cpp: rework handleAnonymousNonLazyPointers() to handle anl's in the middle - the __data section too. - ------ Tagged ld64-52 - -2006-04-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4513304> 64-bit: 9A152 TextEdit crashes in dlopen on bring-up - * src/MachOReaderRelocatable.cpp: rework anonymous non-lazy-pointer detection - -2006-04-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4528054> 64 Bit: Development build of ppc64 TextEdit gets confused about static variables - * src/MachOReaderRelocatable.cpp: mark non-lazy-pointer atoms as scopeTranslationUnit if targetting a static symbol - - - -2006-04-21 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: fix default address for ppc64 custom stack - * src/MachOWriterExecutable.cpp: fix set up of ppc64 custom stack - - -2006-04-14 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: fix -sub_library processing to work it dylib is specifed with leaf name - ------ Tagged ld64-51.1 - -2006-04-13 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4513304> 64-bit: 9A152 TextEdit crashes in dlopen on bring-up - * src/MachOReaderRelocatable.hpp: when detecting anonymous non-lazy-pointers disqualify data - that points to static or global symbols - * src/ld.cpp: print version of ld64 in error messages - - ------ Tagged ld64-51 - -2006-04-11 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4499168> exported symbols not properly stripped - * src/MachOReaderRelocatable.hpp: enable AnonymousAtom::setScope() - -2006-03-31 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4498391> ld64 fails when linking debug ppc64 HIToolbox - * src/MachOReaderRelocatable.hpp: handle anonymous non-lazy pointers encoded with local relocations - * src/MachOWriterExecutable.hpp: in -r mode, only generated INDIRECT_SYMBOL_LOCAL for non-lazy targets that - - -2006-03-31 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4496499> ld64 should remove generated file if link errors out - * src/MachOWriterExecutable.hpp: catch exceptions in Writer<A>::write(), delete output file, and rethrow - - ------ Tagged ld64-50 - - -2006-03-29 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: synthesize .objc_class_name symbols - * src/MachOFileAbstraction.hpp: use strncpy for sect/seg names to zero fill trailing space - -2006-03-28 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fix spurious warning about dwarf line info - ------ Tagged ld64-49.1 - -2006-03-25 Nick Kledzik <kledzik@apple.com> - - * MachOWriterExecutable.hpp : don't complain about ppc64 dyld being based > 4GB - ------ Tagged ld64-49 - -2006-03-24 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: dyld is allowed to have synthesized non-lazy pointers - <rdar://problem/4488113> ld64 is after processing bad GSYM stabs - * src/MachOReaderRelocatable.hpp: if a GSYM is found that does not match any data symbol, suppress it - -2006-03-23 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: in Writer<x86>::fixUpReferenceFinal() fix when x86::kPointer is for an - external relocation - -2006-03-23 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: change macosx-min-version to default to a per-architecture setting - add warning if -pagezero_size is not page aligned - * src/MachOWriterExecutable.hpp: properly handle external relocations for ppc64 with 4GB pagezero - * src/machochecker.cpp: sanity check relocation records - ------ Tagged ld64-48 - -2006-03-21 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4481406> 64bit: passing function pointer to another function passes the wrong function address - * src/MachOReaderRelocatable.hpp: when processing a non-lazy pointer to a static function, don't accidentally - match it to a STAB symbol. - -2006-03-21 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4180168> .eh symbols make up 13% of libstdc++'s stripped binary size - * src/ObjectFile.h: add ReaderOptions.fForFinalLinkedImage - * src/Options.cpp: setup ReaderOptions.fForFinalLinkedImage - * src/MachOReaderRelocatable.hpp: mark .eh symbols kSymbolTableNotIn when building final linked image - -2006-03-21 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4473742> ld64 does not parse optional second argument to -filelist - * unit-tests/test-cases/filelist: added - * src/Options.cpp: in Options::loadFileList() handle comma option - - ------ Tagged ld64-47.1 - - ------ Tagged ld64-47 - - ------ Tagged ld64-46 - -2006-03-10 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4419505> ld64 should figure out architecture from .o files - * unit-tests/test-cases/auto-arch: added - * src/ld.cpp: added Linker::inferArchitecture() to scan .o files are infer architecture to link - * src/MachOReaderArchive.hpp: enhanced validFile() to look deeper into archive and really valdate - * src/MachOWriterExecutable.hpp: stop using fOptions.architecture() - * src/Options.cpp: stop defaulting to ppc64 - - -2006-03-09 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4465004> Need "intentionally left blank" dylib stubs - * unit-tests/include/common.makefile: add VALID_ARCHS - * unit-tests/run-all-unit-tests: set up VALID_ARCHS - * unit-tests/test-cases/blank-stubs: add test case - * src/ld.cpp: in addDylib(), detect and ignore blank stubs - * src/MachOReaderDylib.hpp: in constructor, handle blank stubs - -2006-03-09 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4471424> crash in stub with 2GB pagezero - * src/MachOWriterExecutable.hpp: StubAtom<ppc64> can't be no-pic if a large zero-page is used - -2006-03-06 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: addSectionAlignment, warn if -sectalign alignment is not a power of two - ------ Tagged ld64-45 - - -2006-03-06 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4466930> LP64/9A122: ld64: hang when trying to link DiscRecording framework - * src/Options.cpp: addSectionAlignment, warn on zero. Use log2() for alignment conversion - - ------ Tagged ld64-44 - -2006-03-04 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fix again test for detection of anonymous non-lazy-pointer. - Error out if .o file contains old __DWARFA style dwarf. - -2006-03-02 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: only re-map page aligned sub-parts of a fat file. A conformat mmap() requires alignment. - ------ Tagged ld64-43 - - -2006-03-02 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: <rdar://problem/4464370> tighten detection of anonymous non-lazy-pointer - ------ Tagged ld64-42 - -2006-02-28 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fix x86 __IMPORT permissions for class Segment - -2006-02-28 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4461240> SWB: ld64-37 (can't resolve symbol ___dso_handle) - * src/MachOWriterExecutable.hpp: add class DsoHandleAtom - -2006-02-28 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/literals-coalesce-alignment: added test case - * src/ld.cpp: when coalescing strings pick one with greater alignment - <rdar://problem/4458660> ld64: CG link failed because lo14 reference to anonymous non-lazy-pointer not aligned - * unit-tests/test-cases/relocs-c/test.c: tweak to fail like 4458660 - * src/MachOReaderRelocatable.hpp: detect anonymous non-lazy-pointer and transform into real non-lazy-pointers - ------ Tagged ld64-41 - -2006-02-24 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: Warning about -no_dead_strip_inits_and_terms and -i options. - Fix -weak-l option. - ------ Tagged ld64-40 - -2006-02-24 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4454698> Leopard9A113: ppc64 libstdc++.dylib initializer crashes in pthread_once - * unit-tests/test-cases/multiple-entry-points: added - * src/MachOReaderRelocatable.hpp: make sure that if there are multiple symbols with the same - address, that we properly make zero length atoms for all but last symbol - -2006-02-24 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: <rdar://problem/4456093> ld64 doesn't realpath(3) B&I tracing paths - -2006-02-24 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: <rdar://problem/4457078> 9A110: ld64 can't deal with section names >16 chars - -2006-02-23 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: use vector.reserve() to minimize re-allocations - * src/Options.cpp: use vector.reserve() to minimize re-allocations - * src/MachOReaderRelocatable.hpp: use vector.reserve() to minimize re-allocations - * src/MachOReaderDylib.hpp: use vector.reserve() to minimize re-allocations - * src/ld.cpp: use vector.reserve() to minimize re-allocations - -2006-02-23 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4455927> ld64 creates corrupt executables (and has malloc errors) with -headerpad option - * src/MachOWriterExecutable.hpp: Change LoadCommandsPaddingAtom<A>::setSize() to update fLargestAtomSize - * unit-tests/test-cases/header-pad: added - -2006-02-23 Nick Kledzik <kledzik@apple.com> - - <rdar://problem/4455192> ld64 creates invalid static executables - * src/MachOWriterExecutable.hpp: Change MachHeaderAtom<A>::copyRawContent() to create correct header - for static executables. Change SymbolTableLoadCommandsAtom to skip LC_DYSYMTAB for static executables - * src/machochecker.cpp: Add tests that static executables are well formed - * unit-tests/test-cases/static-executable: added - -2006-02-22 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: <rdar://problem/4453468> chnage printf on unknown arg to a throw - ------ Tagged ld64-39 - -2006-02-20 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/read-only-relocs: added new test case - * src/MachOWriterExecutable.hpp: <rdar://problem/4448922> detect and error on relocs in read-only sections - * src/MachOReaderRelocatable.hpp: fix parsing of i386 absolute addressing relocs - -2006-02-20 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/stabs-coalesce: added new test case - * src/ld.cpp.hpp: <rdar://problem/4449226> in collectStabs removed unused stabs - ------ Tagged ld64-38 - -2006-02-17 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: <rdar://problem/4434578> set correct n_sect field of stabs - -2006-02-15 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderArchive.hpp: <rdar://problem/4441920> with -all_load skip over both kinds of SYMDEFs - * unit-tests/test-cases/archive-basic/Makefile: add -all_load test case - ------ Tagged ld64-37 - -2006-02-13 Eric Christopher <echristo@apple.com> - - * src/MachOWriterExecutable.hpp (assignFileOffsets): Simplify. Add comments. - Adjust whitespace. - -2006-02-13 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: in Writer<x86>::fixUpReferenceRelocatable() fix kPCRel32 for external case - -2006-02-13 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/zero-fill: added - * src/machochecker.cpp: check that S_ZEROFILL have no file offset - * src/MachOWriterExecutable.hpp: rework assignFileOffsets() to fix rdar://problem/4441145 - -2006-02-12 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: <rdar://problem/4440880> fix use of first zero-length c-string in .o file - -2006-02-12 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: <rdar://problem/4440905> fix uninitialized fAlignment - -2006-02-12 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add pointer-diff cases - * src/Architectures.hpp: make size explicit in ppc/ppc64 kPointerDiff - * src/MachOReaderRelocatable.hpp: don't allow kPointerDiff64 for ppc (just ppc64) - * src/MachOWriterExecutable.cpp: set proper r_length for ld -r of kPointerDiff - ------ Tagged ld64-36 - -2006-02-08 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.cpp: rdar://problem/4438677 Handle when a .o file dwarf line info entries but no functions - -2006-02-08 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.cpp: Properly set address of first TEXT section - Keep S_COALESCED attribute for __eh_frame - -2006-02-08 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: Temporarily turn allowable client errors into warnings - * unit-tests/test-cases/allowable-clientMakefile: Temporarily let warnings be ok for above - * src/MachOWriterExecutable.hpp: fix ld -r to not use external relocations for symbols make static - -2006-02-08 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: A sibling in an umbrella can always link with its other siblings - * unit-tests/test-cases/allowable-client: add test case for above - -2006-02-08 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: support LOCAL non-lazy pointers to hidden symbols - * src/machochecker.cpp: verify indirect symbol table - * unit-tests/test-cases/private-non-lazy: added test case - -2006-02-07 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix calculation of file offsets in ld -r mode - * src/machochecker.cpp: verify segment file offsets are within file - ------ Tagged ld64-35 - -2006-02-06 Nick Kledzik <kledzik@apple.com> - - * ld.cpp: allow parent of sub-framework to link - * unit-tests/test-cases/allowable-client/Makefile: added cases for parent and clients of parent - -2006-02-04 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/relocs-c/test.c: added some array cases - * src/MachOReaderRelocatable.hpp: factor out makeReferenceToEH() - * src/MachOWriterExecutable.hpp: add initial support for non-lazy pointer synthesis - ------ Tagged ld64-34 - -2006-02-04 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: <rdar://problem/4432917> fix -no_arch_warnings - <rdar://problem/4432932> fix -undefined warning - Do BINCL/EINCL optimization for gfull stabs - Implement "essential symbols" for stabs (-Sp) - Fix allowable clients to only test on direct libraries - * src/MachOReaderRelocatable.hpp: support BINCL/EINCL stabs - -2006-02-03 Nick Kledzik <kledzik@apple.com> - - * src/machochecker.cpp: add code to check load command alignment - * src/MachOWriterExecutable.hpp: make load command alignment depend on architecture - -2006-02-03 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/literals-coalesce: added - * src/MachOReaderRelocatable.hpp: assure all targets of low14 ppc relocs are at least 4-byte alignmented - ------ Tagged ld64-33 - -2006-02-02 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: properly coalesce 8-byte literals - * src/MachOWriterExecutable.hpp: support ppc64::kPointerDiff32 - ------ Tagged ld64-32 - -2006-02-02 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: support anonymous zero fill atoms - -2006-02-02 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: A weak definition is good enough, do not search archives for a non-weak one - * unit-tests/test-cases/archive-weak: add test case for above - * src/MachOReaderRelocatable.hpp: an atom should never have a by-name reference to itself - * src/Options.cpp: prevent .eh symbols from being exported via a -exported_symbols_list - -2006-02-01 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: Support -macosx_version_min 10.5 - -2006-02-01 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: don't try to parse debug_line dwarf if no symboled atoms - ------ Tagged ld64-31 - -2006-02-01 Eric Christopher <echristo@apple.com> - - * unit-tests/test-cases/allow-stack-execute/Makefile: Move otool handling... - * unit-tests/include/common.makefile: ... here. - * unit-tests/bin/fail-if-stdin.pl: New. - * unit-tests/test-cases/no-uuid: Ditto. - * src/ld.cpp (Linker::) Add fCreateUUID. - (::Linker): Initialize. - (::collectStabs): Use. Set if dwarf or we have a UUID already. - (::writeOutput): Pass as argument to Writer::write along with option. - * src/Options.h (Option::emitUUID): Declare. - (Option::fEmitUUID): Ditto. - * src/Options.cpp (Option::emitUUID): New. - (parse): Handle -no_uuid. - * src/MachOReaderRelocatable (Reader::Reader): Handle LC_UUID. - * src/ExecutableFile.h (Writer::Write): Add createUUID boolean. - * src/MachOWriterExecutable: Add UUID forward declaration. - (fUUIDAtom): New. - (UUIDLoadCommandAtom): Emit LC_UUID if fEmit. New function emit. Size - to zero at start. - (Writer::writer): Add handle for LC_UUID. If createUUID emit LC_UUID. - (MachHeaderAtom::copyRawContent): Don't count a load command if its size is - 0. - (UUIDLoadCommandAtom::copyRawContent): Depend on fEmit. - - -2006-01-31 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/dwarf-debug-notes : Added - * src/ld.cpp: don't generate debug note for .eh symbols - * src/MachOReaderRelocatable.hpp: make dwarf line info to atom matching faster and better - -2006-01-31 Nick Kledzik <kledzik@apple.com> - - * ld64.xcodeproj/project.pbxproj : Make buildable on Leopard - * src/MachOFileAbstraction.hpp: make buildable without latest cctools headers - -2006-01-31 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: better error message for bad relocs - * src/ObjectDump.cpp: add emacs tab settings - * src/SectCreate.h: ditto - * src/SectCreate.cpp: ditto - * src/machochecker.cpp: ditto - * src/ExecutableFile.h: ditto - -2006-01-30 Eric Christopher <echristo@apple.com> - - * src/ExecutableFile.h: Indent. - -2006-01-30 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: performance improvements - * src/ld.cpp: now that stubs are synthesized in write, don't need to special case anymore - -2006-01-30 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fix parsing of pcc relocs - * unit-tests/test-cases/relocs-asm/relocs-asm.s: add test case for above - -2006-01-29 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/weak_import: added test case - * src/ld.cpp: move code for weak_import mismatch to writer - * src/ObjectFile.h: remove ImportWeakness methods - * src/MachOReaderDylib.hpp: ditto - * src/SectCreate.cpp: ditto - * src/Architectures.hpp: add new ReferenceKinds for weak_imports - * src/MachOReaderRelocatable.hpp: implement new ReferenceKinds - * src/MachOWriterExecutable.hpp: handle new ReferenceKinds and weak_import mismatches - -2006-01-29 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp: verify -allow_stack_execute is only used on main executables - -2006-01-29 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: sync with latest dwarf reader from Geoff - * src/debugline.c: sync with latest dwarf reader from Geoff - -2006-01-27 Eric Christopher <echristo@apple.com> - - * src/ld.cpp (Linker::syntesizeStabs): Correct spelling. Update all uses. - -2006-01-27 Eric Christopher <echristo@apple.com> - - * src/Options.h (Options): Add hasExecutableStack, fExecutableStack. - * src/Options.cpp (Options::hasExecutableStack): New. - (Options::parse): Parse -allow_stack_execute. - * src/MachOWriterExecutable.hpp (MachHeaderAtom::copyRawContent): - Implement MH_ALLOW_STACK_EXECUTION. - * unit-tests/include/common.makefile (FAIL_IF_EMPTY): New. - * unit-tests/bin/fail-if-no-stdin.pl: New file. - * unit-tests/test-cases/allow-stack-execute: New directory. - -2006-01-27 Nick Kledzik <kledzik@apple.com> - - * src/MachOFileAbstraction.hpp: rely on latest system headers - * src/MachOWriterExecutable.hpp: fix ppc stubs. - wrote new relocationNeededInFinalLinkedImage() to replace common code - -2006-01-27 Eric Christopher <echristo@apple.com> - - * src/ld.cpp (logTraceInfo): New. - (Linker::addArchive): Use. - (Linker::addDylib): Ditto. - * src/ObjectFile (ReaderOptions::fTraceOutputFile): New. - * src/MachOReaderArchive.hpp (Reader::Reader): Move trace - logging to Linker::addArchive. - * src/Options.cpp (parsePreCommandLineEnvironment): Check - LD_PRINT_FILE if tracing dylibs or archives. - -2006-01-26 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: handle NULL strings in SO debug notes - -2006-01-26 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix header padding calculation and thread state - -2006-01-26 Nick Kledzik <kledzik@apple.com> - - Rewrite all stabs processing. - Move sythesize of debug notes into ld.cpp - -2006-01-26 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix ppc and ppc64 stub relocs - -2006-01-25 Nick Kledzik <kledzik@apple.com> - - * ld64.xcodeproj/project.pbxproj: special case building in Curry - -2006-01-25 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix bugs in stub/lazy-pointer synthesis - -2006-01-24 Eric Christopher <echristo@apple.com> - - * src/ld.cpp (Linker::createReaders): Change logging title to XBS. - (Linker::addDylib): Ditto. - * src/MachOReaderArchive.hpp (Reader::Reader): Ditto. - * src/Options.h (fPrintOptions): New. - * src/Options.cpp (Options::Options): Initialize above. - (Options::checkForFile): Change logging title to XBS. - (Options::findFramework): Ditto. - (Options::parse): Add log for options. - (Options::parsePreCommandLineEnvironmentSettings): Add LD_TRACE_ARCHIVES, - LD_TRACE_DYLIBS, and LD_PRINT_OPTIONS. - -2006-01-24 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: better C++ eh parsing - -2006-01-23 Eric Christopher <echristo@apple.com> - - * unit-tests/bin/fail-if-exit-zero.pl: New. - * unit-tests/include/common.makefile (FAIL_IF_SUCCESS): Use. - * unit-tests/allowable-client: New test. - * src/ld.cpp (Linker::addDylib): Check allowable clients before adding dylib. - * src/Options.h (allowableClients): New. - (clientName): Ditto. - (fAllowableClients): Ditto. - (fClientName): Ditto. - * src/Options.cpp: Implement above. - (parse): Handle -allowable_client and -client_name. - * src/MachOReaderDylib.hpp (getAllowableClients): New. - (fAllowableClients): Ditto. - (Reader): Process LC_SUB_CLIENT load command. - * src/ObjectFile.h (parentUmbrella): New. - (getAllowableClients): New. - * src/MachOWriterExecutable.hpp (AllowableClientLoadCommandsAtom): New. - -2006-01-23 Nick Kledzik <kledzik@apple.com> - - * unit-tests/test-cases/archive-basic: added - * src/ld.cpp: fix shadowed local variable - * src/FileAbstraction.hpp: <rdar://problem/4417372> ld64 shouldn't inline when building debug - -2006-01-23 Nick Kledzik <kledzik@apple.com> - - * src/ld.cpp: fix symbol not found error message - * src/MachOReaderDylib.hpp: add logging to hash table - * src/MachOReaderRelocatable.hpp: enable stabs processing. Handle static functions with stubs - handle labeled cstrings. - * src/MachOWriterExecutable.hpp: properly suppress atoms not in symbol table. fix low14 error check. - add StubAtomHelper. - * unit-tests/test-cases/relocs-literals/test.c: add more interesting edge cases - -2006-01-17 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: tweaks to synthesizing debug notes - -2006-01-16 Nick Kledzik <kledzik@apple.com> - - * src/debugline.{sh}: added - * src/MachOReaderRelocatable.hpp: synthesize debug notes SOL from dwarf - * src/MachOWriterExecutable.hpp: fix lazy pointer section - * src/ObjectDump.hpp: Fix conditionalization - * unit-tests/test-cases/dwarf-strip: added - -2006-01-11 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: support Tiger crt1.o build with old ld64 - * src/ObjectDump.hpp: Support -arch option - -2006-01-10 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: fix stubs for ppc64 - * src/MachOFileAbstraction.hpp: fix typo for macho_routines - * ld64.xcodeproj/project.pbxproj: add machochecker target - * src/machochecker.cpp: new skeleton for checking mach-o file bit - * unit-tests/: Add support for running machochecker - -2006-01-10 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: warn if dwarf can't be parsed - * src/MachOReaderArchive.hpp: modTime for OSO stabs from archives is .a modTime - -2006-01-09 Nick Kledzik <kledzik@apple.com> - - * track modification time of .o files so that sythesized OSO stab will have it - -2006-01-09 Nick Kledzik <kledzik@apple.com> - - * src/MachOFileAbstraction.hpp: add macho_uuid_command - * src/MachOWriterExecutable.cpp: add UUID load command to generated files - -2006-01-09 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderDylib.hpp: no longer keep dylib memory mapped - * src/ld.cpp: don't track dylib sizes because they are not longer memory mapped - -2006-01-05 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: support new relocations - -2006-01-05 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderDylib.hpp: support MH_DYLIB_STUB - * src/MachOReaderRelocatable.hpp: Add Geoff's comp unit extractor - -2006-01-05 Nick Kledzik <kledzik@apple.com> - - refactor: transform Atom::dontStripName() to getSymbolTableInclusion() - * src/ld.cpp: pass dyld_stub_binding_helper to writer - * src/MachOReaderRelocatable.hpp: update synthesized stabs - Ignore stubs and lazy pointers in .o files - Support initializers and terminators - * src/MachOWriterExecutable.hpp: synthesize stubs and lazy pointers as needed - * ld64.xcodeproj/project.pbxproj: change Release target to build with dwarf - -2006-01-03 Eric Christopher <echristo@apple.com> - - * src/Options.h (multipleDefinitionsInDylibs): Declare. - (overridingDefinitionInDependentDylib): Ditto. - (warnOnMultipleDefinitionsInObjectFiles): Ditto. - (multiplyDefined): Remove. - (multiplyDefinedUnused): Ditto. - (fMultiplyDefined): Ditto. - (fWarnOnMultiplyDefined): New. - (fMultiplyDefinedDynamic): Ditto. - * src/Options.cpp (Options::Options): Initialize above. - (overridingDefinitionInDependentDylib): New. - (multipleDefinitionsInDylibs): Ditto. - (warnOnMultipleDefinitionsInObjectFiles): Ditto. - (parse): Update comments. Fix parsing of -y option. - Update error message for -dead_strip. Parse above - options. - -2006-01-02 Nick Kledzik <kledzik@apple.com> - - * Refactor: move Atom::writeContent() to Writer - -2005-12-23 Nick Kledzik <kledzik@apple.com> - - * Reworked, simplify, and document test harness - * unit-tests/README: Added - -2005-12-23 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fixes for Objective-C - * unit-tests/test-cases/relocs-objc: Added - -2005-12-22 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: fix check that next reloc is pair - * src/MachOReaderRelocatable.hpp: Add code to synthesize essential stabs from dwarf - -2005-12-21 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: Fix parsing of literal sections - * src/MachOWriterExecutable.hpp: Fix writing of literal sections - * unit-tests/test-cases/relocs-literals: Added - -2005-12-15 Eric Christopher <echristo@apple.com> - - * src/Options.h (enum Treatment): New. - (enum PICTreatment): Delete. - (enum VersionMin): New. - (prebind): Declare. - (macosxVersionMin): Ditto. - (multiplyDefined): Ditto. - (multiplyDefinedUnused): Ditto. - (setVersionMin): Ditto. - (setPICTreatment): Delete. - (setReadOnlyRelocTreatment): Ditto. - (picTreatment): Adjust return type. - (parseTreatment): New. - (fPrebind): Ditto. - (fVersionMin): Ditto. - (fPICTreatment): Change type. - (fMultiplyDefined): New. - (fMultiplyDefinedUnused): Ditto. - (fLimitUndefinedSymbols): Ditto. - - * src/Options.cpp: Fix whitespace. Add comments on options. - (Options::Options): Add initializers for new variables. - (Options::prebind): New. - (Options::macosxVersionMin): Ditto. - (Options::parseTreatment): Ditto. - (Options::setVersionMin): Ditto. - (Options::setReadOnlyRelocTreatment): Delete. - (Options::setPICTreatment): Ditto. - (Options::Parse): Update for above. Add comments. - -2005-12-15 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: Add comments about dwarf - -2005-12-14 Nick Kledzik <kledzik@apple.com> - - * src/ELFFileAbstraction.hpp: Added - * src/ELFReaderRelocatable.hpp: Added - * Lot of fixes for new architecture - * Added __OPEN_SOURCE__ to "Preprocessor Macros" to disable new architecture support by default - -2005-12-13 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: check for S_ATTR_DEBUG and ignore those sections - * unit-tests/test-cases/dwarf-ignore: added - -2005-12-12 Nick Kledzik <kledzik@apple.com> - - * Added test harness and three initial tests: - relocs-asm, relocs-c, and hello-world - -2005-12-12 Nick Kledzik <kledzik@apple.com> - - * src/MachOReaderRelocatable.hpp: Massive refactoring: - Now there are three Atom classes, Chopping into Atoms - is done on label boundaries or by knowledge of special - sections, Share lots of ppc/ppc64 code. - Stabs process code is temporarily disabled. - -2005-12-12 Nick Kledzik <kledzik@apple.com> - - * src/ObjectDump.cpp: Add command line options: -no_content, -stabs, -no_sort - -2005-12-11 Eric Christopher <echristo@apple.com> - - * src/Options.cpp: Reformat. - * src/Options.h: Ditto. - -2005-12-07 Eric Christopher <echristo@apple.com> - - * src/MachOReaderRelocatable.hpp (Atom::getAlignment): - When calculating alignment of an Atom, take into account - the alignment from which we pulled the Atom. - -2005-12-06 Nick Kledzik <kledzik@apple.com> - - * src/Options.cpp src/Options.h: Add design comments - -2005-12-05 Eric Christopher <echristo@apple.com> - - * src/ld.cpp (Linker::createWriter): Uncomment ppc64 and - i386 linkers. - -2005-12-05 Eric Christopher <echristo@apple.com> - - * ChangeLog: New file. - -2005-12-02 Nick Kledzik <kledzik@apple.com> - - * src/ObjectFile.h: Add design comments - -2005-11-30 Nick Kledzik <kledzik@apple.com> - - * Fix uses of __OPEN_SOURCE__ - -2005-11-28 Nick Kledzik <kledzik@apple.com> - - * Refactor Atom to use getDefinitionKind() - -2005-11-21 Nick Kledzik <kledzik@apple.com> - - * src/MachOWriterExecutable.hpp: don't generate section for commons in -r mode - -2005-11-18 Nick Kledzik <kledzik@apple.com> - - * x86 tweaks - -2005-11-18 Nick Kledzik <kledzik@apple.com> - - * src/ObjectDump.cpp: make work with command line arguments - -2005-11-18 Nick Kledzik <kledzik@apple.com> - - * Massive rework to remove preprocessor conditionals and use templates - -2005-11-14 Nick Kledzik <kledzik@apple.com> - - * Created new Subversion repository for ld64 from cvs tag ld64-27.2 diff --git a/doc/man/man1/dyldinfo.1 b/doc/man/man1/dyldinfo.1 index 3dafedc..92deb13 100644 --- a/doc/man/man1/dyldinfo.1 +++ b/doc/man/man1/dyldinfo.1 @@ -1,4 +1,4 @@ -.Dd November 20, 2008 +.Dd November 10, 2010 .Dt dyldinfo 1 .Os Darwin .Sh NAME @@ -7,12 +7,14 @@ .Sh SYNOPSIS .Nm .Op Fl arch Ar arch-name +.Op Fl dylibs .Op Fl rebase .Op Fl bind .Op Fl weak_bind .Op Fl lazy_bind .Op Fl export .Op Fl opcodes +.Op Fl function_starts .Ar file(s) .Sh DESCRIPTION Executables built for Mac OS X 10.6 and later have a new format for the @@ -23,10 +25,12 @@ The options are as follows: .Bl -tag -width indent .It Fl arch Ar arch Only display the specified architecture. Other architectures in a universal image are ignored. +.It Fl dylibs +Display the table of dylibs on which this image depends. .It Fl rebase Display the table of rebasing information. Rebasing is what dyld does when an image is not loaded at its preferred address. Typically, this involves updating pointers in the __DATA -segment which are point within the image. +segment which point within the image. .It Fl bind Display the table of binding information. These are the symbolic fix ups that dyld must do when an image is loaded. @@ -41,6 +45,8 @@ functions in some external dylib. Display the table symbols which this image exports. .It Fl opcodes Display the low level opcodes used to encode all rebase and binding information. +.It Fl function_starts +Decodes the list of function start addresses. .El .Sh SEE ALSO .Xr otool 1 diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index b6363fc..d4d1329 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -1,4 +1,4 @@ -.Dd December 15, 2008 +.Dd Sept 2, 2010 .Dt ld 1 .Os Darwin .Sh NAME @@ -78,7 +78,7 @@ paths. .Ss Two-level namespace By default all references resolved to a dynamic library record the library to which they were resolved. At runtime, dyld uses that information to directly resolve -symobls. The alternative is to use the -flat_namespace option. With flat namespace, +symbols. The alternative is to use the -flat_namespace option. With flat namespace, the library is not recorded. At runtime, dyld will search each dynamic library in load order when resolving symbols. This is slower, but more like how other operating systems resolve symbols. @@ -114,7 +114,7 @@ Merges object files to produce another mach-o object file with file type MH_OBJE .It Fl dylinker Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when building dyld. .It Fl dynamic -The default. Implied by -dynamiclib, -bundle, or -execute +The default. Implied by -dylib, -bundle, or -execute .It Fl static Produces a mach-o file that does not use the dyld. Only used building the kernel. .It Fl arch Ar arch_name @@ -149,12 +149,18 @@ the first function in it is called. This is the same as listing a file name path to a shared library on the link line except that the linker will construct glue code so that the shared library is not loaded until the first function in it is called. +.It Fl upward-l Ns Ar x +This is the same as the -lx but specifies that the dylib is an upward dependency. +.It Fl upward_library Ar path_to_library +This is the same as listing a file name path to a library on the link line but also marks +the dylib as an upward dependency. .It Fl L Ns dir Add .Ar dir to the list of directories in which to search for libraries. Directories specified with -L are searched in the order they appear on the command line -and before the default search path. +and before the default search path. In Xcode4 and later, there can be a space between +the -L and directory. .It Fl Z Do not search the standard directories when searching for libraries and frameworks. .It Fl syslibroot Ar rootdir @@ -162,10 +168,15 @@ Prepend .Ar rootdir to all search paths when searching for libraries or frameworks. .It Fl search_paths_first -By default the -lx and -weak-lx options first search for a file of the form `libx.dylib' in each directory +This is now the default (in Xcode4 tools). When processing -lx the linker now searches each directory +in its library search paths for `libx.dylib' then `libx.a' before the moving on to the next path +in the library search path. +.It Fl search_dylibs_first +Changes the searching behavior for libraries. The default is that when processing -lx the linker +searches each directory in its library search paths for `libx.dylib' then `libx.a'. +This option changes the behavior to first search for a file of the form `libx.dylib' in each directory in the library search path, then a file of the form `libx.a' is searched for in the library search paths. -This option changes it so that in each path `libx.dylib' is searched for then `libx.a' before the -next path in the library search path is searched. +This option restores the search behavior of the linker prior to Xcode4. .It Fl framework Ar name[,suffix] This option tells the linker to search for `name.framework/name' the framework search path. If the optional suffix is specified the framework is first searched for the name with the suffix and then without @@ -181,13 +192,17 @@ This was previously done with a separate -sub_umbrella option. This is the same as the -framework name[,suffix] except that the linker will construct glue code so that the framework is not loaded until the first function in it is called. You cannot directly access -data or Objective-C classes in a frameworked linked this way. +data or Objective-C classes in a framework linked this way. +.It Fl upward_framework Ar name[,suffix] +This is the same as the -framework name[,suffix] but also specifies that the +framework is an upward dependency. .It Fl F Ns dir Add .Ar dir to the list of directories in which to search for frameworks. Directories specified with -F are searched in the order they appear on the command line -and before the default search path. +and before the default search path. In Xcode4 and later, there can be a space between +the -F and directory. .It Fl all_load Loads all members of static archive libraries. .It Fl ObjC @@ -231,7 +246,7 @@ any symbol in that section that are specified in the order file is moved to the start of its section and laid out in the same order as in the order file .Ar file . Order files are text files with one symbol name per line. Lines starting with a # are comments. -A symbol name may be optionally preceded with its object file leafname and a colon (e.g. foo.o:_foo). +A symbol name may be optionally preceded with its object file leaf name and a colon (e.g. foo.o:_foo). This is useful for static functions/data that occur in multiple files. A symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or ppc:foo.o:_foo). This enables you to have one order file that works for multiple architectures. @@ -249,6 +264,11 @@ This is set to indicate the oldest Mac OS X version that that the output is to b a later version enables the linker to assumes features of that OS in the output file. The format of .Ar version is a Mac OS X version number such as 10.4 or 10.5 +.It Fl ios_version_min Ar version +This is set to indicate the oldest iOS version that that the output is to be used on. Specifying +a later version enables the linker to assumes features of that OS in the output file. The format of +.Ar version +is an iOS version number such as 3.1 or 4.0 .It Fl image_base Ar address Specifies the perferred load address for a dylib or bundle. The argument .Ar address @@ -321,11 +341,11 @@ will cause a bus error if a NULL pointer is dereferenced. The argument is a hexadecimal number with an optional leading 0x. If .Ar size is zero, the linker will not generate a page zero segment. By default on 32-bit architectures the page zero size -is 4KB. On 64-bit architectures, the default size if 4GB. The ppc64 architecture has some special cases. Since Mac +is 4KB. On 64-bit architectures, the default size is 4GB. The ppc64 architecture has some special cases. Since Mac OS X 10.4 did not support 4GB page zero programs, the default page zero size for ppc64 will be 4KB unless -macosx_version_min is 10.5 or later. Also, the -mdynamic-no-pic codegen model for ppc64 will only work if the code is placed in the lower 2GB of the address space, so the if the linker detects any such code, the page zero -size is set to 4KB and then a new unredable trailing segment is created after the code, filling up the lower 4GB. +size is set to 4KB and then a new unreadable trailing segment is created after the code, filling up the lower 4GB. .It Fl stack_size Ar size Specifies the maximum stack size for the main thread in a program. Without this option a program has a 8MB stack. The argument @@ -353,7 +373,7 @@ dynamic libraries the bundle was linked with. Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather leave them as private external in the resulting object file. .It Fl d -Force definition of common symbols. That is, transform tentative defintions into real definitions. +Force definition of common symbols. That is, transform tentative definitions into real definitions. .El .Ss Options that control symbol resolution .Bl -tag @@ -391,6 +411,11 @@ The specified is added to the list of global symbols names that will not remain as global symbols in the output file. This option can be used multiple times. For short lists, this can be more convenient than creating a file and using -unexported_symbols_list. +.It Fl reexported_symbols_list Ar file +The specified +.Ar filename +contains a list of symbol names that are implemented in a dependent dylib and should be re-exported +through the dylib being created. .It Fl alias Ar symbol_name Ar alternate_symbol_name Create an alias named .Ar alternate_symbol_name @@ -432,7 +457,7 @@ Specifies how commons (aka tentative definitions) are resolved with respect to d ignore_dylibs, use_dylibs, error. The default is ignore_dylibs which means the linker will turn a tentative definition in an object file into a real definition and not even check dylibs for conflicts. The dylibs option means the linker should check linked dylibs for definitions and use them to replace tentative definitions -from object files. The error option means the linker should issu an error whenever a tentative definition in an +from object files. The error option means the linker should issue an error whenever a tentative definition in an object file conflicts with an external symbol in a linked dylib. See also -warn_commons. .El .Ss Options for introspecting the linker @@ -463,7 +488,7 @@ Do not put debug information (STABS or DWARF) in the output file. .It Fl x Do not put non-global symbols in the output file's symbol table. Non-global symbols are useful when debugging and getting symbol names in back traces, but are not used at runtime. If -x is used with -r -non-global symbol names are not removed, but instead replaced with a unique, duumy name +non-global symbol names are not removed, but instead replaced with a unique, dummy name that will be automatically removed when linked into a final linked image. This allows dead code stripping, which uses symbols to break up code and data, to work properly and provides the security of having source symbol names removed. @@ -488,6 +513,10 @@ Prints the version of the linker. Normally when targeting Mac OS X 10.6, the linker will generate compact information in the __LINKEDIT segment. This option causes the linker to instead produce traditional relocation information. +.It Fl allow_heap_execute +Normally i386 main executables will be marked so that the Mac OS X 10.7 and later kernel +will only allow pages with the x-bit to execute instructions. This option overrides that +behavior and allows instructions on any page to be run. .It Fl no_eh_labels Normally in -r mode, the linker produces .eh labels on all FDEs in the __eh_frame section. This option suppresses those labels. Those labels are not needed by the Mac OS X 10.6 @@ -497,13 +526,23 @@ When producing a final linked image, the linker processes the __eh_frame section produces an __unwind_info section. Most FDE entries in the __eh_frame can be represented by a 32-bit value in the __unwind_info section. The option issues a warning for any function whose FDE cannot be expressed in the compact unwind format. +.It Fl warn_weak_exports +Issue a warning if the resulting final linked image contains weak external symbols. Such +symbols require dyld to do extra work at launch time to coalesce those symbols. +.It Fl objc_gc_compaction +Marks the Objective-C image info in the final linked image with the bit that says that the +code was built to work the compacting garbage collection. +.It Fl objc_gc +Verifies all code was compiled with -fobjc-gc or -fobjc-gc-only. +.It Fl objc_gc_only +Verifies all code was compiled with -fobjc-gc-only. .It Fl dead_strip_dylibs Remove dylibs that are unreachable by the entry point or exported symbols. That is, suppresses the generation of load command commands for dylibs which supplied no symbols during the link. This option should not be used when linking against a dylib which is required at runtime for some indirect reason such as the dylib has an important initializer. .It Fl allow_sub_type_mismatches -Normally the linker consisders different cpu-subtype for ARM (e.g. armv4t and armv6) to be different +Normally the linker considers different cpu-subtype for ARM (e.g. armv4t and armv6) to be different different architectures that cannot be mixed at build time. This option relaxes that requirement, allowing you to mix object files compiled for different ARM subtypes. .It Fl no_uuid @@ -522,7 +561,13 @@ Only used when creating a dynamic library. .It Fl sub_umbrella Ar framework_name The specified framework will be re-exported. Only used when creating a dynamic library. .It Fl allowable_client Ar name -Restricts what can link against the dynamic library being created. +Restricts what can link against the dynamic library being created. By default any code +can link against any dylib. But if a dylib is supposed to be private to a small +set of clients, you can formalize that by adding a -allowable_client for each client. +If a client is libfoo.1.dylib its -allowable_client name would be "foo". If a +client is Foo.framework its -allowable_client name would be "Foo". For the degenerate +case where you want no one to ever link against a dylib, you can set the +-allowable_client to "!". .It Fl client_name Ar name Enables a bundle to link against a dylib that was built with -allowable_client. The name specified must match one of the -allowable_client names specified when the dylib was created. @@ -601,11 +646,11 @@ driver when it is invoked with multiple -arch arguments. Specifies that hints should be added to the resulting binary that can help speed up runtime binding by dyld as long as the libraries being linked against have not changed. .It Fl dot Ar path -Create a file a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. +Create a file at the specified path containing a graph of symbol dependencies. The .dot file can be viewed in GraphViz. .It Fl keep_relocs Add section based relocation records to a final linked image. These relocations are ignored at runtime by dyld. .It Fl warn_stabs -Print a warning when the linker cannot do a BINCL/EINCL optimzation because the compiler put a bad stab symbol inside +Print a warning when the linker cannot do a BINCL/EINCL optimization because the compiler put a bad stab symbol inside a BINCL/EINCL range. .It Fl warn_commons Print a warning whenever the a tentative definition in an object file is found and a external symbol by the same name @@ -614,7 +659,7 @@ in a header file. .It Fl read_only_stubs [i386 only] Makes the __IMPORT segment of a final linked images read-only. This option makes a program slightly more secure in that the JMP instructions in the i386 fast stubs cannot be easily overwritten by malicious code. The downside -is the dyld must use mprotect() to temporily make the segment writable while it is binding the stubs. +is the dyld must use mprotect() to temporarily make the segment writable while it is binding the stubs. .It Fl slow_stubs [i386 only] Instead of using single JMP instruction stubs, the linker creates code in the __TEXT segment which calls through a lazy pointer in the __DATA segment. @@ -626,6 +671,22 @@ is linked such that _malloc is interposable, then calls to malloc() from within stub and could potentially indirected to an alternate malloc. If libSystem.dylib were built without making _malloc interposable then if _malloc was interposed at runtime, calls to malloc from with libSystem would be missed (not interposed) because they would be direct calls. +.It Fl no_function_starts +By default the linker creates a compress table of function start addresses in the LINKEDIT of +final linked image. This option disables that behavior. +.It Fl no_version_load_command +By default the linker creates a load command in final linked images that contains the -macosx_version_min. +This option disables that behavior. +.It Fl no_objc_category_merging +By default when producing final linked image, the linker will optimize Objective-C classes by merging +any categories on a class into the class. Both the class and its categories must be defined in the image +being linked for the optimization to occur. Using this option disables that behavior. +.It Fl object_path_lto Ar filename +When performing Link Time Optimization (LTO) and a temporary mach-o object file is needed, if this +option is used, the temporary file will be stored at the specified path and remain after the link +is complete. Without the option, the linker picks a path and deletes the object file before the linker +tool completes, thus tools such as the debugger or dsymutil will not be able to access the DWARF debug +info in the temporary object file. .El .Ss Obsolete Options .Bl -tag @@ -663,7 +724,7 @@ This is now the default so does not need to be specified. .It Fl multi_module Multi-modules in dynamic libraries have been ignored at runtime since Mac OS X 10.4.0. This option is obsolete. .It Fl no_dead_strip_inits_and_terms -The linker never dead strips initialzation and termination routines. They are considered "roots" of the dead strip graph. +The linker never dead strips initialization and termination routines. They are considered "roots" of the dead strip graph. .It Fl A Ar basefile Obsolete incremental load format. This option is obsolete. .It Fl b @@ -691,7 +752,7 @@ is used. This was previously used to debug where an undefined symbol was used, automatically prints out all usages. The -why_live option can also be used to display what kept a symbol from being dead striped. This option is obsolete. .It Fl Y Ar number -Used to control how many occurances of each symbol specifed with -y would be shown. This option is obsolete. +Used to control how many occurrences of each symbol specified with -y would be shown. This option is obsolete. .It Fl nomultidefs Only used when linking an umbrella framework. Sets the MH_NOMULTIDEFS bit in the mach_header. The MH_NOMULTIDEFS bit has been obsolete since Mac OS X 10.4. This option is obsolete. diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 9c5493c..cfb54eb 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ F96904890A4333AC00B77D2A /* PBXTargetDependency */, F9EA73970974999B008B4F1D /* PBXTargetDependency */, F9B693890EC4D28C00076912 /* PBXTargetDependency */, + F9F9AD68116D58AF0028EFAB /* PBXTargetDependency */, ); name = "unit-tests"; productName = "unit-tests"; @@ -43,13 +44,35 @@ /* Begin PBXBuildFile section */ F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; + F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93CB246116E69EB003233B8 /* tlvp.cpp */; }; F97F5029070D0BB200B9FCD7 /* ld.1 in copy man page */ = {isa = PBXBuildFile; fileRef = F97F5028070D0BB200B9FCD7 /* ld.1 */; }; + F98498A310AE2159009E9878 /* compact_unwind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA963310A2545C0097A440 /* compact_unwind.cpp */; }; + F98498A410AE2159009E9878 /* got.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AB1063107D380700E54C9E /* got.cpp */; }; + F9849E3610B38EF5009E9878 /* order_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9849E3410B38EF5009E9878 /* order_file.cpp */; }; + F984A38210BB4B0D009E9878 /* branch_island.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F984A38010BB4B0D009E9878 /* branch_island.cpp */; }; + F989D30D106826020014B60C /* OutputFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F989D30B106826020014B60C /* OutputFile.cpp */; }; F9A3DDD30ED762E400C590B9 /* PruneTrie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */; }; F9A3DE1E0ED7738300C590B9 /* prune_trie.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */; }; + F9A4DB9110F816FF00BD8423 /* objc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A4DB8F10F816FF00BD8423 /* objc.cpp */; }; + F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA44DA1294885F00CB8390 /* branch_shim.cpp */; }; + F9AA65111051BD2B003E3539 /* stubs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65101051BD2B003E3539 /* stubs.cpp */; }; + F9AA65891051E750003E3539 /* macho_relocatable_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */; }; + F9AA65DD1051EC4A003E3539 /* archive_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65D71051EC4A003E3539 /* archive_file.cpp */; }; + F9AA65DE1051EC4A003E3539 /* lto_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65D91051EC4A003E3539 /* lto_file.cpp */; }; + F9AA65DF1051EC4A003E3539 /* macho_dylib_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */; }; + F9AA6786105700C2003E3539 /* opaque_section_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA6784105700C2003E3539 /* opaque_section_file.cpp */; }; + F9AA67B610570C41003E3539 /* dtrace_dof.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA67B510570C41003E3539 /* dtrace_dof.cpp */; }; + F9AA687C10572E27003E3539 /* InputFiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA687A10572E27003E3539 /* InputFiles.cpp */; }; + F9AA69B610583C0C003E3539 /* SymbolTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA69B410583C0C003E3539 /* SymbolTable.cpp */; }; + F9AA69C110583E19003E3539 /* Resolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA69BF10583E19003E3539 /* Resolver.cpp */; }; + F9AA6FF910618CD2003E3539 /* macho_relocatable_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */; }; + F9AE20FF1107D1440007ED5D /* dylibs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AE20FD1107D1440007ED5D /* dylibs.cpp */; }; + F9AE23291109015E0007ED5D /* lto_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9AA65D91051EC4A003E3539 /* lto_file.cpp */; }; F9B1A2640A3A563E00DA8FAB /* rebase.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B1A2580A3A448800DA8FAB /* rebase.1 */; }; F9B670120DDA17E800E6D0DA /* UnwindDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */; }; F9B813850EC2657800F94C13 /* unwinddump.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9B813810EC2653000F94C13 /* unwinddump.1 */; }; F9BA51650ECE58C800D1D62E /* dyldinfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */; }; + F9BA955E10A233000097A440 /* huge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BA955C10A233000097A440 /* huge.cpp */; }; F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C0D48A06DD1E1B001C7193 /* Options.cpp */; }; F9C12EA30ED63DE7005BC69D /* dyldinfo.1 in install man page */ = {isa = PBXBuildFile; fileRef = F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */; }; F9EA72D5097454FF008B4F1D /* machochecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EA72D4097454FF008B4F1D /* machochecker.cpp */; }; @@ -61,7 +84,7 @@ /* Begin PBXBuildRule section */ F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc; + compilerSpec = com.apple.compilers.llvm.clang.1_0; fileType = sourcecode.c; isEditable = 1; outputFiles = ( @@ -69,7 +92,7 @@ }; F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc; + compilerSpec = com.apple.compilers.llvm.clang.1_0; fileType = sourcecode.cpp; isEditable = 1; outputFiles = ( @@ -148,6 +171,13 @@ remoteGlobalIDString = F9EA72CA097454A6008B4F1D; remoteInfo = machocheck; }; + F9F9AD67116D58AF0028EFAB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9023C3006D5A227001BBF46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9BA51600ECE58BE00D1D62E; + remoteInfo = dyldinfo; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -208,26 +238,63 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3DA587190ACC53BE0015C432 /* LTOReader.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = LTOReader.hpp; path = src/ld/LTOReader.hpp; sourceTree = "<group>"; }; C02A29DE0953B26E001FB8C1 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; }; F9023C3906D5A23E001BBF46 /* ld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9023C3E06D5A254001BBF46 /* ExecutableFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ExecutableFile.h; path = src/ld/ExecutableFile.h; sourceTree = "<group>"; }; F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = "<group>"; }; - F9023C4106D5A254001BBF46 /* ObjectFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ObjectFile.h; path = src/ld/ObjectFile.h; sourceTree = "<group>"; }; + F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = "<group>"; }; F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = "<group>"; }; F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = "<group>"; }; F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = "<group>"; }; - F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderDylib.hpp; path = src/ld/MachOReaderDylib.hpp; sourceTree = "<group>"; }; - F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOReaderRelocatable.hpp; path = src/ld/MachOReaderRelocatable.hpp; sourceTree = "<group>"; }; - F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOWriterExecutable.hpp; path = src/ld/MachOWriterExecutable.hpp; sourceTree = "<group>"; }; + F93CB246116E69EB003233B8 /* tlvp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tlvp.cpp; sourceTree = "<group>"; }; + F93CB247116E69EB003233B8 /* tlvp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tlvp.h; sourceTree = "<group>"; }; F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; F971EED706D5AD240041D381 /* ObjectDump.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ObjectDump.cpp; path = src/other/ObjectDump.cpp; sourceTree = "<group>"; }; F97F5028070D0BB200B9FCD7 /* ld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = ld.1; path = doc/man/man1/ld.1; sourceTree = "<group>"; }; - F98D26850AA779BD00416316 /* OpaqueSection.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = OpaqueSection.hpp; path = src/ld/OpaqueSection.hpp; sourceTree = "<group>"; }; - F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ArchiveReader.hpp; path = src/ld/ArchiveReader.hpp; sourceTree = "<group>"; }; + F984963310AB9318009E9878 /* stub_ppc_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_ppc_classic.hpp; sourceTree = "<group>"; }; + F9849E3410B38EF5009E9878 /* order_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = order_file.cpp; sourceTree = "<group>"; }; + F9849E3510B38EF5009E9878 /* order_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = order_file.h; sourceTree = "<group>"; }; + F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm_classic.hpp; sourceTree = "<group>"; }; + F984A38010BB4B0D009E9878 /* branch_island.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_island.cpp; sourceTree = "<group>"; }; + F984A38110BB4B0D009E9878 /* branch_island.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = branch_island.h; sourceTree = "<group>"; }; + F989D0391062E6350014B60C /* stub_x86_64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64.hpp; sourceTree = "<group>"; }; + F989D30B106826020014B60C /* OutputFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OutputFile.cpp; path = src/ld/OutputFile.cpp; sourceTree = "<group>"; }; + F989D30C106826020014B60C /* OutputFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OutputFile.h; path = src/ld/OutputFile.h; sourceTree = "<group>"; }; + F989D3AA10684F5B0014B60C /* LinkEdit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEdit.hpp; path = src/ld/LinkEdit.hpp; sourceTree = "<group>"; }; + F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = LinkEditClassic.hpp; path = src/ld/LinkEditClassic.hpp; sourceTree = "<group>"; }; + F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = HeaderAndLoadCommands.hpp; path = src/ld/HeaderAndLoadCommands.hpp; sourceTree = "<group>"; }; F9A3DDCA0ED762B700C590B9 /* libprunetrie.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libprunetrie.a; sourceTree = BUILT_PRODUCTS_DIR; }; F9A3DDD20ED762E400C590B9 /* PruneTrie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PruneTrie.cpp; path = src/other/PruneTrie.cpp; sourceTree = "<group>"; }; F9A3DE0F0ED76D1900C590B9 /* prune_trie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prune_trie.h; path = src/other/prune_trie.h; sourceTree = "<group>"; }; + F9A4DB8F10F816FF00BD8423 /* objc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = objc.cpp; sourceTree = "<group>"; }; + F9A4DB9010F816FF00BD8423 /* objc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = objc.h; sourceTree = "<group>"; }; + F9AA44DA1294885F00CB8390 /* branch_shim.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = branch_shim.cpp; sourceTree = "<group>"; }; + F9AA44DB1294885F00CB8390 /* branch_shim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = branch_shim.h; sourceTree = "<group>"; }; + F9AA5FCC103F5CD1003E3539 /* ld.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ld.hpp; path = src/ld/ld.hpp; sourceTree = "<group>"; }; + F9AA650D1051BD2B003E3539 /* make_stubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = make_stubs.h; sourceTree = "<group>"; }; + F9AA650F1051BD2B003E3539 /* stub_arm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm.hpp; sourceTree = "<group>"; }; + F9AA65101051BD2B003E3539 /* stubs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stubs.cpp; sourceTree = "<group>"; }; + F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_relocatable_file.cpp; sourceTree = "<group>"; }; + F9AA65881051E750003E3539 /* macho_relocatable_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_relocatable_file.h; sourceTree = "<group>"; }; + F9AA65D71051EC4A003E3539 /* archive_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = archive_file.cpp; sourceTree = "<group>"; }; + F9AA65D81051EC4A003E3539 /* archive_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive_file.h; sourceTree = "<group>"; }; + F9AA65D91051EC4A003E3539 /* lto_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lto_file.cpp; sourceTree = "<group>"; }; + F9AA65DA1051EC4A003E3539 /* lto_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lto_file.h; sourceTree = "<group>"; }; + F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_dylib_file.cpp; sourceTree = "<group>"; }; + F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_dylib_file.h; sourceTree = "<group>"; }; + F9AA6784105700C2003E3539 /* opaque_section_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_section_file.cpp; sourceTree = "<group>"; }; + F9AA6785105700C2003E3539 /* opaque_section_file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opaque_section_file.h; sourceTree = "<group>"; }; + F9AA67B410570C41003E3539 /* dtrace_dof.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dtrace_dof.h; sourceTree = "<group>"; }; + F9AA67B510570C41003E3539 /* dtrace_dof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dtrace_dof.cpp; sourceTree = "<group>"; }; + F9AA687A10572E27003E3539 /* InputFiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InputFiles.cpp; path = src/ld/InputFiles.cpp; sourceTree = "<group>"; }; + F9AA687B10572E27003E3539 /* InputFiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InputFiles.h; path = src/ld/InputFiles.h; sourceTree = "<group>"; }; + F9AA69B410583C0C003E3539 /* SymbolTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SymbolTable.cpp; path = src/ld/SymbolTable.cpp; sourceTree = "<group>"; }; + F9AA69B510583C0C003E3539 /* SymbolTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolTable.h; path = src/ld/SymbolTable.h; sourceTree = "<group>"; }; + F9AA69BF10583E19003E3539 /* Resolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Resolver.cpp; path = src/ld/Resolver.cpp; sourceTree = "<group>"; }; + F9AA69C010583E19003E3539 /* Resolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Resolver.h; path = src/ld/Resolver.h; sourceTree = "<group>"; }; + F9AB1063107D380700E54C9E /* got.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = got.cpp; sourceTree = "<group>"; }; + F9AB1064107D380700E54C9E /* got.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = got.h; sourceTree = "<group>"; }; + F9AE20FD1107D1440007ED5D /* dylibs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dylibs.cpp; sourceTree = "<group>"; }; + F9AE20FE1107D1440007ED5D /* dylibs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dylibs.h; sourceTree = "<group>"; }; F9B1A2580A3A448800DA8FAB /* rebase.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = rebase.1; path = doc/man/man1/rebase.1; sourceTree = "<group>"; }; F9B670080DDA176100E6D0DA /* unwinddump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unwinddump; sourceTree = BUILT_PRODUCTS_DIR; }; F9B670110DDA17E800E6D0DA /* UnwindDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindDump.cpp; path = src/other/unwinddump.cpp; sourceTree = "<group>"; }; @@ -235,6 +302,12 @@ F9B813BF0EC27C6700F94C13 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOTrie.hpp; path = src/abstraction/MachOTrie.hpp; sourceTree = "<group>"; }; F9BA515B0ECE58AA00D1D62E /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = src/other/dyldinfo.cpp; sourceTree = "<group>"; }; F9BA51610ECE58BE00D1D62E /* dyldinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyldinfo; sourceTree = BUILT_PRODUCTS_DIR; }; + F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_classic.hpp; sourceTree = "<group>"; }; + F9BA8A7F1096150F0097A440 /* stub_x86.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86.hpp; sourceTree = "<group>"; }; + F9BA955C10A233000097A440 /* huge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = huge.cpp; sourceTree = "<group>"; }; + F9BA955D10A233000097A440 /* huge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = huge.h; sourceTree = "<group>"; }; + F9BA963310A2545C0097A440 /* compact_unwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_unwind.cpp; sourceTree = "<group>"; }; + F9BA963410A2545C0097A440 /* compact_unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind.h; sourceTree = "<group>"; }; F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = "<group>"; }; F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = "<group>"; }; F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = "<group>"; }; @@ -318,6 +391,69 @@ name = Products; sourceTree = "<group>"; }; + F9AA650B1051BD2B003E3539 /* passes */ = { + isa = PBXGroup; + children = ( + F984A38010BB4B0D009E9878 /* branch_island.cpp */, + F984A38110BB4B0D009E9878 /* branch_island.h */, + F9AA44DA1294885F00CB8390 /* branch_shim.cpp */, + F9AA44DB1294885F00CB8390 /* branch_shim.h */, + F9849E3410B38EF5009E9878 /* order_file.cpp */, + F9849E3510B38EF5009E9878 /* order_file.h */, + F9BA963310A2545C0097A440 /* compact_unwind.cpp */, + F9BA963410A2545C0097A440 /* compact_unwind.h */, + F9AA67B410570C41003E3539 /* dtrace_dof.h */, + F9AA67B510570C41003E3539 /* dtrace_dof.cpp */, + F9BA955C10A233000097A440 /* huge.cpp */, + F9BA955D10A233000097A440 /* huge.h */, + F9AB1063107D380700E54C9E /* got.cpp */, + F9AB1064107D380700E54C9E /* got.h */, + F93CB246116E69EB003233B8 /* tlvp.cpp */, + F93CB247116E69EB003233B8 /* tlvp.h */, + F9AE20FD1107D1440007ED5D /* dylibs.cpp */, + F9AE20FE1107D1440007ED5D /* dylibs.h */, + F9A4DB8F10F816FF00BD8423 /* objc.cpp */, + F9A4DB9010F816FF00BD8423 /* objc.h */, + F9AA650C1051BD2B003E3539 /* stubs */, + ); + name = passes; + path = src/ld/passes; + sourceTree = "<group>"; + }; + F9AA650C1051BD2B003E3539 /* stubs */ = { + isa = PBXGroup; + children = ( + F9AA650D1051BD2B003E3539 /* make_stubs.h */, + F9AA65101051BD2B003E3539 /* stubs.cpp */, + F9AA650F1051BD2B003E3539 /* stub_arm.hpp */, + F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */, + F9BA8A7F1096150F0097A440 /* stub_x86.hpp */, + F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */, + F989D0391062E6350014B60C /* stub_x86_64.hpp */, + F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */, + F984963310AB9318009E9878 /* stub_ppc_classic.hpp */, + ); + path = stubs; + sourceTree = "<group>"; + }; + F9AA65861051E750003E3539 /* parsers */ = { + isa = PBXGroup; + children = ( + F9AA6784105700C2003E3539 /* opaque_section_file.cpp */, + F9AA6785105700C2003E3539 /* opaque_section_file.h */, + F9AA65D71051EC4A003E3539 /* archive_file.cpp */, + F9AA65D81051EC4A003E3539 /* archive_file.h */, + F9AA65D91051EC4A003E3539 /* lto_file.cpp */, + F9AA65DA1051EC4A003E3539 /* lto_file.h */, + F9AA65DB1051EC4A003E3539 /* macho_dylib_file.cpp */, + F9AA65DC1051EC4A003E3539 /* macho_dylib_file.h */, + F9AA65871051E750003E3539 /* macho_relocatable_file.cpp */, + F9AA65881051E750003E3539 /* macho_relocatable_file.h */, + ); + name = parsers; + path = src/ld/parsers; + sourceTree = "<group>"; + }; F9B8137E0EC2651200F94C13 /* doc */ = { isa = PBXGroup; children = ( @@ -342,18 +478,24 @@ F9B813AD0EC27B8500F94C13 /* ld */ = { isa = PBXGroup; children = ( + F9AA69BF10583E19003E3539 /* Resolver.cpp */, + F9AA69C010583E19003E3539 /* Resolver.h */, + F9AA69B410583C0C003E3539 /* SymbolTable.cpp */, + F9AA69B510583C0C003E3539 /* SymbolTable.h */, + F9AA687A10572E27003E3539 /* InputFiles.cpp */, + F9AA687B10572E27003E3539 /* InputFiles.h */, + F9AA5FCC103F5CD1003E3539 /* ld.hpp */, F9023C3F06D5A254001BBF46 /* ld.cpp */, - F933DC37092A82480083EAC8 /* Architectures.hpp */, - F99F63CE0D99A291007F5394 /* ArchiveReader.hpp */, - F933E3CD092E84250083EAC8 /* MachOReaderRelocatable.hpp */, - F933E3CC092E84250083EAC8 /* MachOReaderDylib.hpp */, - F933E3CE092E84250083EAC8 /* MachOWriterExecutable.hpp */, - 3DA587190ACC53BE0015C432 /* LTOReader.hpp */, - F9023C3E06D5A254001BBF46 /* ExecutableFile.h */, - F9023C4106D5A254001BBF46 /* ObjectFile.h */, - F98D26850AA779BD00416316 /* OpaqueSection.hpp */, F9C0D48A06DD1E1B001C7193 /* Options.cpp */, F9C0D48B06DD1E1B001C7193 /* Options.h */, + F989D30B106826020014B60C /* OutputFile.cpp */, + F989D30C106826020014B60C /* OutputFile.h */, + F989D7E91072DEC20014B60C /* HeaderAndLoadCommands.hpp */, + F989D3AA10684F5B0014B60C /* LinkEdit.hpp */, + F989D44B10694F2E0014B60C /* LinkEditClassic.hpp */, + F9AA650B1051BD2B003E3539 /* passes */, + F9AA65861051E750003E3539 /* parsers */, + F933DC37092A82480083EAC8 /* Architectures.hpp */, F9EA7582097882F3008B4F1D /* debugline.c */, F9EA7583097882F3008B4F1D /* debugline.h */, ); @@ -381,7 +523,7 @@ isa = PBXNativeTarget; buildConfigurationList = F933D91B09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ld" */; buildPhases = ( - 0B12F6A50CE39466008ABCAE /* build configure.h */, + F9E8DB4D11921594007B4D6A /* make config.h */, F9023C3606D5A23E001BBF46 /* Sources */, F9023C3706D5A23E001BBF46 /* Frameworks */, F97F5025070D0B6300B9FCD7 /* copy man page */, @@ -501,9 +643,19 @@ /* Begin PBXProject section */ F9023C3006D5A227001BBF46 /* Project object */ = { isa = PBXProject; + attributes = { + ORGANIZATIONNAME = "Apple Inc."; + }; buildConfigurationList = F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 0; + knownRegions = ( + English, + Japanese, + French, + German, + ); mainGroup = F9023C2C06D5A227001BBF46; productRefGroup = F9023C3A06D5A23E001BBF46 /* Products */; projectDirPath = ""; @@ -523,34 +675,34 @@ /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ - 0B12F6A50CE39466008ABCAE /* build configure.h */ = { + F96D5367094A2754008E9EE8 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "build configure.h"; outputPaths = ( - "$(DERIVED_FILE_DIR)/configure.h", ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "if [ -f /Developer/usr/local/include/llvm-c/lto.h ]; then\n\techo \"#define LTO_SUPPORT 1\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib\" > ${DERIVED_FILE_DIR}/linker_opts\nelse\n\techo \"#undef LTO_SUPPORT\" > ${DERIVED_FILE_DIR}/configure.h\n\techo \"\" > ${DERIVED_FILE_DIR}/linker_opts\nfi\n"; + shellPath = /bin/csh; + shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0"; showEnvVarsInLog = 0; }; - F96D5367094A2754008E9EE8 /* ShellScript */ = { + F9E8DB4D11921594007B4D6A /* make config.h */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "make config.h"; outputPaths = ( + "$(DERIVED_FILE_DIR)/configure.h", ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/csh; - shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0"; + shellPath = /bin/sh; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/configure.h\n\nif [ -n \"${IPHONEOS_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_IPHONEOS_MIN_VERSION \\\"${IPHONEOS_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\nelse\n if [ -n \"${MACOSX_DEPLOYMENT_TARGET}\" ]; then\n\techo \"#define DEFAULT_MACOSX_MIN_VERSION \\\"${MACOSX_DEPLOYMENT_TARGET}\\\"\" >> ${DERIVED_FILE_DIR}/configure.h\n fi\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -560,9 +712,29 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, + F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, + F9AA65891051E750003E3539 /* macho_relocatable_file.cpp in Sources */, + F9AA65DD1051EC4A003E3539 /* archive_file.cpp in Sources */, + F9AA65DE1051EC4A003E3539 /* lto_file.cpp in Sources */, + F9AA65DF1051EC4A003E3539 /* macho_dylib_file.cpp in Sources */, F9EA7584097882F3008B4F1D /* debugline.c in Sources */, + F9AA687C10572E27003E3539 /* InputFiles.cpp in Sources */, + F9AA69B610583C0C003E3539 /* SymbolTable.cpp in Sources */, + F9AA69C110583E19003E3539 /* Resolver.cpp in Sources */, + F989D30D106826020014B60C /* OutputFile.cpp in Sources */, + F9AA65111051BD2B003E3539 /* stubs.cpp in Sources */, + F9AA6786105700C2003E3539 /* opaque_section_file.cpp in Sources */, + F9AA67B610570C41003E3539 /* dtrace_dof.cpp in Sources */, + F98498A310AE2159009E9878 /* compact_unwind.cpp in Sources */, + F98498A410AE2159009E9878 /* got.cpp in Sources */, + F9BA955E10A233000097A440 /* huge.cpp in Sources */, + F9849E3610B38EF5009E9878 /* order_file.cpp in Sources */, + F984A38210BB4B0D009E9878 /* branch_island.cpp in Sources */, + F9A4DB9110F816FF00BD8423 /* objc.cpp in Sources */, + F9AE20FF1107D1440007ED5D /* dylibs.cpp in Sources */, + F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */, + F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -570,6 +742,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F9AA6FF910618CD2003E3539 /* macho_relocatable_file.cpp in Sources */, + F9AE23291109015E0007ED5D /* lto_file.cpp in Sources */, F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */, F9EA75BC09788857008B4F1D /* debugline.c in Sources */, ); @@ -668,12 +842,19 @@ target = F9EA72CA097454A6008B4F1D /* machocheck */; targetProxy = F9EA73960974999B008B4F1D /* PBXContainerItemProxy */; }; + F9F9AD68116D58AF0028EFAB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9BA51600ECE58BE00D1D62E /* dyldinfo */; + targetProxy = F9F9AD67116D58AF0028EFAB /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ F933D91C09291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = NO; @@ -682,6 +863,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = DEBUG; GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -696,7 +878,7 @@ GCC_WARN_MISSING_PARENTHESES = YES; GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; GCC_WARN_PEDANTIC = NO; - GCC_WARN_SHADOW = NO; + GCC_WARN_SHADOW = YES; GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNINITIALIZED_AUTOS = NO; @@ -714,7 +896,7 @@ LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; - OTHER_LDFLAGS = "@$(DERIVED_FILE_DIR)/linker_opts"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; PREBINDING = NO; PRODUCT_NAME = ld; SECTORDER_FLAGS = ""; @@ -726,14 +908,20 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_DYNAMIC_NO_PIC = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 3; - GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", + NDEBUG, + "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))", + ); + GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'"; GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; @@ -765,8 +953,7 @@ INSTALL_PATH = /usr/bin; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; OTHER_LDFLAGS = ( - "-Wl,-no_pie", - "@$(DERIVED_FILE_DIR)/linker_opts", + "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -783,12 +970,25 @@ F933D92009291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include"; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + ); INSTALL_PATH = "$(HOME)/bin"; OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; OTHER_REZFLAGS = ""; @@ -811,7 +1011,11 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = s; - HEADER_SEARCH_PATHS = "$(DEVELOPER_DIR)/usr/local/include"; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + ); INSTALL_PATH = "$(HOME)/bin"; OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; OTHER_REZFLAGS = ""; @@ -859,6 +1063,205 @@ }; name = Release; }; + F9849FF810B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_DYNAMIC_NO_PIC = NO; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = "Release-assert"; + }; + F9849FF910B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = all; + ZERO_LINK = NO; + }; + name = "Release-assert"; + }; + F9849FFA10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_64_BIT_PRE_XCODE_3_1 = x86_64; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 3; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1)", + "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))", + ); + GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_TARGET_1 = "LD_VERS='\"ld64-$(RC_ProjectSourceVersion)\"'"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_POINTER_SIGNEDNESS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; + GCC_WARN_UNUSED_VALUE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DEVELOPER_DIR)/usr/local/include", + "$(DEVELOPER_DIR)/usr/include", + ); + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CPLUSPLUSFLAGS)"; + OTHER_LDFLAGS = ( + "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib", + "-Wl,-exported_symbol,__mh_execute_header", + ); + PREBINDING = NO; + PRODUCT_NAME = ld; + SECTORDER_FLAGS = ""; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = "x86_64 i386 ppc"; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = "-Wall"; + }; + name = "Release-assert"; + }; + F9849FFB10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = "$(GCC_PREPROCESSOR_DEFINITIONS_$(RC_RELEASE))"; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = rebase; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = "i386 ppc x86_64"; + }; + name = "Release-assert"; + }; + F9849FFC10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = unwinddump; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + }; + name = "Release-assert"; + }; + F9849FFD10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/src/ld", + "$(DEVELOPER_DIR)/usr/local/include", + ); + INSTALL_PATH = "$(HOME)/bin"; + OTHER_LDFLAGS = "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib"; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = ObjectDump; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = "Release-assert"; + }; + F9849FFE10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ""; + INSTALL_PATH = "$(HOME)/bin"; + PREBINDING = NO; + PRODUCT_NAME = machocheck; + }; + name = "Release-assert"; + }; + F9849FFF10B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/bin; + OTHER_LDFLAGS = "-Wl,-exported_symbol,__mh_execute_header"; + PREBINDING = NO; + PRODUCT_NAME = dyldinfo; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + ZERO_LINK = NO; + }; + name = "Release-assert"; + }; + F984A00010B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = prunetrie; + }; + name = "Release-assert"; + }; + F984A00110B5DE8E009E9878 /* Release-assert */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + PRODUCT_NAME = "unit-tests"; + }; + name = "Release-assert"; + }; F9A3DDCB0ED762B800C590B9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1059,90 +1462,100 @@ buildConfigurations = ( F933D91C09291AC90083EAC8 /* Debug */, F933D91D09291AC90083EAC8 /* Release */, + F9849FFA10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F933D91F09291AC90083EAC8 /* Build configuration list for PBXNativeTarget "ObjectDump" */ = { isa = XCConfigurationList; buildConfigurations = ( F933D92009291AC90083EAC8 /* Debug */, F933D92109291AC90083EAC8 /* Release */, + F9849FFD10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F933D92309291AC90083EAC8 /* Build configuration list for PBXProject "ld64" */ = { isa = XCConfigurationList; buildConfigurations = ( F933D92409291AC90083EAC8 /* Debug */, F933D92509291AC90083EAC8 /* Release */, + F9849FF810B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F96D536D094A2773008E9EE8 /* Build configuration list for PBXAggregateTarget "unit-tests" */ = { isa = XCConfigurationList; buildConfigurations = ( F96D536E094A2773008E9EE8 /* Debug */, F96D536F094A2773008E9EE8 /* Release */, + F984A00110B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9A3DDCF0ED762C100C590B9 /* Build configuration list for PBXNativeTarget "libprunetrie" */ = { isa = XCConfigurationList; buildConfigurations = ( F9A3DDCB0ED762B800C590B9 /* Debug */, F9A3DDCC0ED762B800C590B9 /* Release */, + F984A00010B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */ = { isa = XCConfigurationList; buildConfigurations = ( F9B1A26D0A3A568700DA8FAB /* Debug */, F9B1A26E0A3A568700DA8FAB /* Release */, + F9849FF910B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9B670050DDA176100E6D0DA /* Build configuration list for PBXNativeTarget "unwinddump" */ = { isa = XCConfigurationList; buildConfigurations = ( F9B670060DDA176100E6D0DA /* Debug */, F9B670070DDA176100E6D0DA /* Release */, + F9849FFC10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9BA516D0ECE58DA00D1D62E /* Build configuration list for PBXNativeTarget "dyldinfo" */ = { isa = XCConfigurationList; buildConfigurations = ( F9BA51630ECE58BF00D1D62E /* Debug */, F9BA51640ECE58BF00D1D62E /* Release */, + F9849FFF10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9EA72CF097454D5008B4F1D /* Build configuration list for PBXNativeTarget "machocheck" */ = { isa = XCConfigurationList; buildConfigurations = ( F9EA72D0097454D5008B4F1D /* Debug */, F9EA72D1097454D5008B4F1D /* Release */, + F9849FFE10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; F9EC77F00A2F8616002A3E39 /* Build configuration list for PBXNativeTarget "rebase" */ = { isa = XCConfigurationList; buildConfigurations = ( F9EC77F10A2F8616002A3E39 /* Debug */, F9EC77F20A2F8616002A3E39 /* Release */, + F9849FFB10B5DE8E009E9878 /* Release-assert */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = "Release-assert"; }; /* End XCConfigurationList section */ }; diff --git a/src/abstraction/FileAbstraction.hpp b/src/abstraction/FileAbstraction.hpp index 1f7a629..8517e87 100644 --- a/src/abstraction/FileAbstraction.hpp +++ b/src/abstraction/FileAbstraction.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -118,6 +118,7 @@ class Pointer32 { public: typedef uint32_t uint_t; + typedef int32_t sint_t; typedef _E E; static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } @@ -130,6 +131,7 @@ class Pointer64 { public: typedef uint64_t uint_t; + typedef int64_t sint_t; typedef _E E; static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index 50982a1..a9a69a7 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,6 +40,10 @@ // stuff that will eventually go away once newer cctools headers are widespread +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + #ifndef CPU_SUBTYPE_ARM_V5TEJ #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) #endif @@ -132,6 +136,79 @@ #endif +#ifndef S_THREAD_LOCAL_REGULAR + #define S_THREAD_LOCAL_REGULAR 0x11 +#endif + +#ifndef S_THREAD_LOCAL_ZEROFILL + #define S_THREAD_LOCAL_ZEROFILL 0x12 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLES + #define S_THREAD_LOCAL_VARIABLES 0x13 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLE_POINTERS + #define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +#endif + +#ifndef S_THREAD_LOCAL_INIT_FUNCTION_POINTERS + #define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 +#endif + +#ifndef MH_HAS_TLV_DESCRIPTORS + #define MH_HAS_TLV_DESCRIPTORS 0x800000 +#endif + +#ifndef X86_64_RELOC_TLV + #define X86_64_RELOC_TLV 9 +#endif + +#define GENERIC_RLEOC_TLV 5 + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif + +// type internal to linker +#define BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB 0 + +#ifndef LC_VERSION_MIN_MACOSX + #define LC_VERSION_MIN_MACOSX 0x24 + #define LC_VERSION_MIN_IPHONEOS 0x25 + + struct version_min_command { + uint32_t cmd; /* LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS */ + uint32_t cmdsize; /* sizeof(struct min_version_command) */ + uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t reserved; /* zero */ + }; +#endif + +#ifndef N_SYMBOL_RESOLVER + #define N_SYMBOL_RESOLVER 0x100 +#endif + +#ifndef LC_FUNCTION_STARTS + #define LC_FUNCTION_STARTS 0x26 +#endif + +#ifndef MH_NO_HEAP_EXECUTION + #define MH_NO_HEAP_EXECUTION 0x1000000 +#endif + +#ifndef LC_DYLD_ENVIRONMENT + #define LC_DYLD_ENVIRONMENT 0x27 +#endif + +// hack until newer <mach-o/arm/reloc.h> everywhere +#define ARM_RELOC_HALF 8 +#define ARM_RELOC_HALF_SECTDIFF 9 + // // This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness @@ -471,7 +548,7 @@ public: void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } const uint8_t* uuid() const INLINE { return fields.uuid; } - void set_uuid(uint8_t u[16]) INLINE { memcpy(&fields.uuid, u, 16); } + void set_uuid(const uint8_t u[16]) INLINE { memcpy(&fields.uuid, u, 16); } typedef typename P::E E; private: @@ -1159,6 +1236,29 @@ private: }; +// +// mach-o version load command +// +template <typename P> +class macho_version_min_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t version() const INLINE { return fields.version; } + void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } + + uint32_t reserved() const INLINE { return fields.reserved; } + void set_reserved(uint32_t value) INLINE { E::set32(fields.reserved, value); } + + typedef typename P::E E; +private: + version_min_command fields; +}; + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/src/abstraction/MachOTrie.hpp b/src/abstraction/MachOTrie.hpp index 43284c4..7b30cad 100644 --- a/src/abstraction/MachOTrie.hpp +++ b/src/abstraction/MachOTrie.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,9 +25,11 @@ #define __MACH_O_TRIE__ #include <algorithm> +#include <assert.h> #include "MachOFileAbstraction.hpp" + namespace mach_o { namespace trie { @@ -42,25 +44,28 @@ struct Edge struct Node { - Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), + Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), + fOther(0), fImportedName(NULL), fOrdered(false), fHaveExportInfo(false), fTrieOffset(0) {} ~Node() { } const char* fCummulativeString; std::vector<Edge> fChildren; uint64_t fAddress; - uint32_t fFlags; + uint64_t fFlags; + uint64_t fOther; + const char* fImportedName; bool fOrdered; bool fHaveExportInfo; uint32_t fTrieOffset; - void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { + void addSymbol(const char* fullStr, uint64_t address, uint64_t flags, uint64_t other, const char* importName) { const char* partialStr = &fullStr[strlen(fCummulativeString)]; for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { Edge& e = *it; int subStringLen = strlen(e.fSubString); if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { // already have matching edge, go down that path - e.fChild->addSymbol(fullStr, address, flags); + e.fChild->addSymbol(fullStr, address, flags, other, importName); return; } else { @@ -81,18 +86,30 @@ struct Node abEdge.fChild = bNode; Edge bcEdge(bcEdgeStr, cNode); bNode->fChildren.push_back(bcEdge); - bNode->addSymbol(fullStr, address, flags); + bNode->addSymbol(fullStr, address, flags, other, importName); return; } } } } + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + assert(importName != NULL); + assert(other != 0); + } + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + assert(other != 0); + } // no commonality with any existing child, make a new edge that is this whole string Node* newNode = new Node(strdup(fullStr)); Edge newEdge(strdup(partialStr), newNode); fChildren.push_back(newEdge); newNode->fAddress = address; newNode->fFlags = flags; + newNode->fOther = other; + if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && (importName != NULL) && (strcmp(fullStr,importName) != 0) ) + newNode->fImportedName = importName; + else + newNode->fImportedName = NULL; newNode->fHaveExportInfo = true; } @@ -115,14 +132,26 @@ struct Node } // byte for terminal node size in bytes, or 0x00 if not terminal node - // teminal node (uleb128 flags, uleb128 addr) + // teminal node (uleb128 flags, uleb128 addr [uleb128 other]) // byte for child node count // each child: zero terminated substring, uleb128 node offset bool updateOffset(uint32_t& offset) { - uint32_t nodeSize = 1; // byte for length of export info - if ( fHaveExportInfo ) - nodeSize += uleb128_size(fFlags) + uleb128_size(fAddress); - + uint32_t nodeSize = 1; // length of export info when no export info + if ( fHaveExportInfo ) { + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + nodeSize = uleb128_size(fFlags) + uleb128_size(fOther); // ordinal + if ( fImportedName != NULL ) + nodeSize += strlen(fImportedName); + ++nodeSize; // trailing zero in imported name + } + else { + nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + nodeSize += uleb128_size(fOther); + } + // do have export info, overall node size so far is uleb128 of export info + export info + nodeSize += uleb128_size(nodeSize); + } // add children ++nodeSize; // byte for count of chidren for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { @@ -139,13 +168,42 @@ struct Node void appendToStream(std::vector<uint8_t>& out) { if ( fHaveExportInfo ) { - // nodes with export info: size, flags, address - out.push_back(uleb128_size(fFlags) + uleb128_size(fAddress)); - append_uleb128(fFlags, out); - append_uleb128(fAddress, out); + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( fImportedName != NULL ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1; + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + append_string(fImportedName, out); + } + else { + // nodes with re-export info: size, flags, ordinal, empty-string + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + 1; + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fOther, out); + out.push_back(0); + } + } + else if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // nodes with export info: size, flags, address, other + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress) + uleb128_size(fOther); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + append_uleb128(fOther, out); + } + else { + // nodes with export info: size, flags, address + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); + out.push_back(nodeSize); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + } } else { - // no export info + // no export info uleb128 of zero is one byte of zero out.push_back(0); } // write number of children @@ -216,22 +274,25 @@ struct Entry const char* name; uint64_t address; uint64_t flags; + uint64_t other; + const char* importName; }; -inline void makeTrie(const std::vector<Entry>& input, std::vector<uint8_t>& output) + +inline void makeTrie(const std::vector<Entry>& entries, std::vector<uint8_t>& output) { Node start(strdup("")); // make nodes for all exported symbols - for (std::vector<Entry>::const_iterator it = input.begin(); it != input.end(); ++it) { - start.addSymbol(it->name, it->address, it->flags); + for (std::vector<Entry>::const_iterator it = entries.begin(); it != entries.end(); ++it) { + start.addSymbol(it->name, it->address, it->flags, it->other, it->importName); } // create vector of nodes std::vector<Node*> orderedNodes; - orderedNodes.reserve(input.size()*2); - for (std::vector<Entry>::const_iterator it = input.begin(); it != input.end(); ++it) { + orderedNodes.reserve(entries.size()*2); + for (std::vector<Entry>::const_iterator it = entries.begin(); it != entries.end(); ++it) { start.addOrderedNodes(it->name, orderedNodes); } @@ -263,18 +324,31 @@ struct EntryWithOffset static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, - char* cummulativeString, int curStrOffset, std::vector<EntryWithOffset>& output) + char* cummulativeString, int curStrOffset, + std::vector<EntryWithOffset>& output) { if ( p >= end ) throw "malformed trie, node past end"; - const uint8_t terminalSize = *p++; + const uint8_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { EntryWithOffset e; e.nodeOffset = p-start; e.entry.name = strdup(cummulativeString); e.entry.flags = read_uleb128(p, end); - e.entry.address = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + e.entry.address = 0; + e.entry.other = read_uleb128(p, end); // dylib ordinal + e.entry.importName = (char*)p; + } + else { + e.entry.address = read_uleb128(p, end); + if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + e.entry.other = read_uleb128(p, end); + else + e.entry.other = 0; + e.entry.importName = NULL; + } output.push_back(e); } const uint8_t childrenCount = *children++; @@ -293,8 +367,8 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector<Entry>& output) -{ - // empty tree has no entries +{ + // empty trie has no entries if ( start == end ) return; char cummulativeString[4000]; diff --git a/src/ld/Architectures.hpp b/src/ld/Architectures.hpp index 2a449f1..1145550 100644 --- a/src/ld/Architectures.hpp +++ b/src/ld/Architectures.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -34,58 +34,26 @@ struct ppc { typedef Pointer32<BigEndian> P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, - kPointerDiff16, kPointerDiff32, kPointerDiff=kPointerDiff32, kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct ppc64 { typedef Pointer64<BigEndian> P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, - kPointerDiff16, kPointerDiff32, kPointerDiff64, kPointerDiff=kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct x86 { typedef Pointer32<LittleEndian> P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32=kPointerDiff, kPointerDiff16, - kPCRel32, kPCRel32WeakImport, kAbsolute32, kPCRel16, kPCRel8, - kImageOffset32, kPointerDiff24, kSectionOffset24, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct x86_64 { typedef Pointer64<LittleEndian> P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointer32, kPointerWeakImport, kPointerDiff, kPointerDiff32, - kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, - kBranchPCRel32, kBranchPCRel32WeakImport, - kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, - kPCRel32GOT, kPCRel32GOTWeakImport, kBranchPCRel8, kGOTNoFixUp, - kImageOffset32, kPointerDiff24, kSectionOffset24, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; struct arm { typedef Pointer32<LittleEndian> P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kGroupSubordinate, kPointer, kPointerWeakImport, kPointerDiff, - kPointerDiff32=kPointerDiff, kReadOnlyPointer, kPointerDiff12, - kBranch24, kBranch24WeakImport, kThumbBranch22, kThumbBranch22WeakImport, - kDtraceProbe, kDtraceProbeSite, kDtraceIsEnabledSite, kDtraceTypeReference }; }; #endif // __ARCHITECTURES__ diff --git a/src/ld/ArchiveReader.hpp b/src/ld/ArchiveReader.hpp deleted file mode 100644 index b8f1a5c..0000000 --- a/src/ld/ArchiveReader.hpp +++ /dev/null @@ -1,467 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_ARCHIVE__ -#define __OBJECT_FILE_ARCHIVE__ - -#include <stdint.h> -#include <math.h> -#include <unistd.h> -#include <sys/param.h> -#include <mach-o/ranlib.h> -#include <ar.h> - -#include <vector> -#include <set> -#include <algorithm> -#include <ext/hash_map> - -#include "MachOFileAbstraction.hpp" -#include "ObjectFile.h" -#include "MachOReaderRelocatable.hpp" -#if LTO_SUPPORT - #include "LTOReader.hpp" -#endif - -namespace archive { - -typedef const struct ranlib* ConstRanLibPtr; - -template <typename A> -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength); - Reader(const uint8_t fileContent[], uint64_t fileLength, - const char* path, time_t modTime, - const LibraryOptions& archiveOptions, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime(){ return fModTime; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector<class ObjectFile::Atom*>& getAtoms(); - virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name); - virtual std::vector<Stab>* getStabs() { return NULL; } - virtual bool optimize(const std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&, - std::vector<const char*>&, const std::set<ObjectFile::Atom*>&, - std::vector<ObjectFile::Atom*>& newDeadAtoms, - uint32_t, ObjectFile::Reader* writer, - ObjectFile::Atom* entryPointAtom, - const std::vector<const char*>& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs); - -private: - static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength); - static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength); - static cpu_type_t architecture(); - - - class Entry : ar_hdr - { - public: - const char* getName() const; - time_t getModTime() const; - const uint8_t* getContent() const; - uint32_t getContentSize() const; - const Entry* getNext() const; - private: - bool hasLongName() const; - unsigned int getLongNameSpace() const; - - }; - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap; - - typedef typename A::P P; - typedef typename A::P::E E; - - const struct ranlib* ranlibHashSearch(const char* name); - ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); - void dumpTableOfContents(); - void buildHashTable(); - - const char* fPath; - time_t fModTime; - const ObjectFile::ReaderOptions& fOptions; - uint32_t fOrdinalBase; - const uint8_t* fFileContent; - uint64_t fFileLength; - const struct ranlib* fTableOfContents; - uint32_t fTableOfContentCount; - const char* fStringPool; - std::vector<class ObjectFile::Atom*> fAllAtoms; - std::vector<class ObjectFile::Reader*> fInstantiatedReaders; - std::set<const class Entry*> fInstantiatedEntries; - std::set<const class Entry*> fPossibleEntries; - NameToEntryMap fHashTable; - bool fForceLoad; - - static std::vector<class ObjectFile::Atom*> fgEmptyList; -}; - -template <typename A> -std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList; - - -template <typename A> -bool Reader<A>::Entry::hasLongName() const -{ - return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); -} - -template <typename A> -unsigned int Reader<A>::Entry::getLongNameSpace() const -{ - char* endptr; - long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); - return result; -} - -template <typename A> -const char* Reader<A>::Entry::getName() const -{ - if ( this->hasLongName() ) { - int len = this->getLongNameSpace(); - static char longName[256]; - strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); - longName[len] = '\0'; - return longName; - } - else { - static char shortName[20]; - strncpy(shortName, this->ar_name, 16); - shortName[16] = '\0'; - char* space = strchr(shortName, ' '); - if ( space != NULL ) - *space = '\0'; - return shortName; - } -} - -template <typename A> -time_t Reader<A>::Entry::getModTime() const -{ - char temp[14]; - strncpy(temp, this->ar_date, 12); - temp[12] = '\0'; - char* endptr; - return (time_t)strtol(temp, &endptr, 10); -} - - -template <typename A> -const uint8_t* Reader<A>::Entry::getContent() const -{ - if ( this->hasLongName() ) - return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); - else - return ((uint8_t*)this) + sizeof(ar_hdr); -} - - -template <typename A> -uint32_t Reader<A>::Entry::getContentSize() const -{ - char temp[12]; - strncpy(temp, this->ar_size, 10); - temp[10] = '\0'; - char* endptr; - long size = strtol(temp, &endptr, 10); - // long name is included in ar_size - if ( this->hasLongName() ) - size -= this->getLongNameSpace(); - return size; -} - - -template <typename A> -const class Reader<A>::Entry* Reader<A>::Entry::getNext() const -{ - const uint8_t* p = this->getContent() + getContentSize(); - p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align - return (class Reader<A>::Entry*)p; -} - - -template <> cpu_type_t Reader<ppc>::architecture() { return CPU_TYPE_POWERPC; } -template <> cpu_type_t Reader<ppc64>::architecture() { return CPU_TYPE_POWERPC64; } -template <> cpu_type_t Reader<x86>::architecture() { return CPU_TYPE_I386; } -template <> cpu_type_t Reader<x86_64>::architecture() { return CPU_TYPE_X86_64; } -template <> cpu_type_t Reader<arm>::architecture() { return CPU_TYPE_ARM; } - - -template <typename A> -bool Reader<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength) -{ - return mach_o::relocatable::Reader<A>::validFile(fileContent); -} - -template <typename A> -bool Reader<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength) -{ -#if LTO_SUPPORT - return lto::Reader::validFile(fileContent, fileLength, architecture()); -#else - return false; -#endif -} - - - -template <typename A> -bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength) -{ - // must have valid archive header - if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) - return false; - - // peak at first .o file and verify it is correct architecture - const Entry* const start = (Entry*)&fileContent[8]; - const Entry* const end = (Entry*)&fileContent[fileLength]; - for (const Entry* p=start; p < end; p = p->getNext()) { - const char* memberName = p->getName(); - // skip option table-of-content member - if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) - continue; - // archive is valid if first .o file is valid - return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize())); - } - // empty archive - return true; -} - -template <typename A> -Reader<A>::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, - const LibraryOptions& archiveOptions, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL), - fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL), fForceLoad(archiveOptions.fForceLoad) -{ - fPath = strdup(path); - fFileContent = fileContent; - fFileLength = fileLength; - - if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) - throw "not an archive"; - - // write out path for -whatsloaded option - if ( options.fLogAllFiles ) - printf("%s\n", path); - - if ( !options.fFullyLoadArchives && !fForceLoad ) { - const Entry* const firstMember = (Entry*)&fFileContent[8]; - if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { - const uint8_t* contents = firstMember->getContent(); - uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); - fTableOfContents = (const struct ranlib*)&contents[4]; - fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); - fStringPool = (const char*)&contents[ranlibArrayLen+8]; - if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength]) - || ((uint8_t*)fStringPool > &fileContent[fileLength]) ) - throw "malformed archive, perhaps wrong architecture"; - this->buildHashTable(); - } - else - throw "archive has no table of contents"; - } -} - - -template <typename A> -ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member) -{ - const char* memberName = member->getName(); - char memberPath[strlen(fPath) + strlen(memberName)+4]; - strcpy(memberPath, fPath); - strcat(memberPath, "("); - strcat(memberPath, memberName); - strcat(memberPath, ")"); - //fprintf(stderr, "using %s from %s\n", memberName, fPath); - try { - // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive - uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent; - if ( validMachOFile(member->getContent(), member->getContentSize()) ) { - return new typename mach_o::relocatable::Reader<A>::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase); - } -#if LTO_SUPPORT - else if ( validLTOFile(member->getContent(), member->getContentSize()) ) { - return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture()); - } -#endif - throwf("archive member '%s' with length %d is not mach-o or bitcode", memberName, member->getContentSize()); - } - catch (const char* msg) { - throwf("in %s, %s", memberPath, msg); - } -} - - -template <typename A> -std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms() -{ - if ( fOptions.fFullyLoadArchives || fForceLoad ) { - // build vector of all atoms from all .o files in this archive - const Entry* const start = (Entry*)&fFileContent[8]; - const Entry* const end = (Entry*)&fFileContent[fFileLength]; - for (const Entry* p=start; p < end; p = p->getNext()) { - const char* memberName = p->getName(); - if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) - continue; - if ( fOptions.fWhyLoad ) { - if ( fForceLoad ) - printf("-force_load forced load of %s(%s)\n", this->getPath(), memberName); - else - printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName); - } - ObjectFile::Reader* r = this->makeObjectReaderForMember(p); - std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms(); - fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); - fInstantiatedReaders.push_back(r); - } - return fAllAtoms; - } - else if ( fOptions.fLoadAllObjcObjectsFromArchives ) { - // build vector of all atoms from all .o files containing objc classes in this archive - for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) { - if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) { - const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)]; - if ( fInstantiatedEntries.count(member) == 0 ) { - if ( fOptions.fWhyLoad ) - printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName()); - // only return these atoms once - fInstantiatedEntries.insert(member); - ObjectFile::Reader* r = makeObjectReaderForMember(member); - std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms(); - fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); - fInstantiatedReaders.push_back(r); - } - } - } - return fAllAtoms; - } - else { - // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed - return fgEmptyList; - } -} - -template <typename A> -bool Reader<A>::optimize(const std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms, - std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>& deadAtoms, - std::vector<ObjectFile::Atom*>& newDeadAtoms, - uint32_t nextOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, - const std::vector<const char*>& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) -{ - bool result = false; - for(std::vector<ObjectFile::Reader*>::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) { - result |= (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, newDeadAtoms, nextOrdinal, - writer, entryPointAtom, llvmOptions, allGlobalsAReDeadStripRoots, okind, - verbose, saveTemps, outputFilePath, pie, allowTextRelocs); - } - return result; -} - - - -template <typename A> -ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name) -{ - class NameToEntryMap::iterator pos = fHashTable.find(name); - if ( pos != fHashTable.end() ) - return pos->second; - else - return NULL; -} - -template <typename A> -void Reader<A>::buildHashTable() -{ - // walk through list backwards, adding/overwriting entries - // this assures that with duplicates those earliest in the list will be found - for (int i = fTableOfContentCount-1; i >= 0; --i) { - const struct ranlib* entry = &fTableOfContents[i]; - const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; - const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)]; - //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); - fHashTable[entryName] = entry; - fPossibleEntries.insert(member); - } -} - -template <typename A> -void Reader<A>::dumpTableOfContents() -{ - for (unsigned int i=0; i < fTableOfContentCount; ++i) { - const struct ranlib* e = &fTableOfContents[i]; - printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName()); - } -} - -template <typename A> -std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name) -{ - if ( fOptions.fFullyLoadArchives || fForceLoad ) { - return NULL; - } - else { - const struct ranlib* result = NULL; - // do a hash search of table of contents looking for requested symbol - result = ranlibHashSearch(name); - if ( result != NULL ) { - const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; - if ( fInstantiatedEntries.count(member) == 0 ) { - if ( fOptions.fWhyLoad ) - printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); - // only return these atoms once - fInstantiatedEntries.insert(member); - ObjectFile::Reader* r = makeObjectReaderForMember(member); - fInstantiatedReaders.push_back(r); - return new std::vector<class ObjectFile::Atom*>(r->getAtoms()); - } - } - //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); - return NULL; - } -} - - - - - -}; // namespace archive - - -#endif // __OBJECT_FILE_ARCHIVE__ diff --git a/src/ld/ExecutableFile.h b/src/ld/ExecutableFile.h deleted file mode 100644 index dca22f7..0000000 --- a/src/ld/ExecutableFile.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __EXECUTABLEFILE__ -#define __EXECUTABLEFILE__ - -#include <stdint.h> -#include <vector> - -#include "ObjectFile.h" -#include "Options.h" - - -namespace ExecutableFile { - - struct DyLibUsed - { - ObjectFile::Reader* reader; - LibraryOptions options; - }; - - class Writer : public ObjectFile::Reader - { - public: - virtual ~Writer() {}; - - virtual const char* getPath() = 0; - virtual std::vector<class ObjectFile::Atom*>& getAtoms() = 0; - virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) = 0; - virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses) = 0; - virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name) = 0; - virtual void addSynthesizedAtoms(const std::vector<class ObjectFile::Atom*>& existingAtoms, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool biggerThanTwoGigs, - uint32_t dylibSymbolCount, - std::vector<class ObjectFile::Atom*>& newAtoms) = 0; - virtual uint64_t write(std::vector<class ObjectFile::Atom*>& atoms, - std::vector<class ObjectFile::Reader::Stab>& stabs, - class ObjectFile::Atom* entryPointAtom, - bool createUUID, bool canScatter, - ObjectFile::Reader::CpuConstraint cpuConstraint, - std::set<const class ObjectFile::Atom*>& atomsThatOverrideWeak, - bool hasExternalWeakDefinitions) = 0; - - protected: - Writer(std::vector<DyLibUsed>&) {}; - }; - -}; - -#endif // __EXECUTABLEFILE__ diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp new file mode 100644 index 0000000..2c97dba --- /dev/null +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -0,0 +1,1391 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __HEADER_LOAD_COMMANDS_HPP__ +#define __HEADER_LOAD_COMMANDS_HPP__ + +#include <stdlib.h> +#include <limits.h> +#include <unistd.h> +#include <mach-o/loader.h> + +#include <vector> + +#include "MachOFileAbstraction.hpp" +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + +class HeaderAndLoadCommandsAbtract : public ld::Atom +{ +public: + HeaderAndLoadCommandsAbtract(const ld::Section& sect, ld::Atom::Definition d, + ld::Atom::Combine c, ld::Atom::Scope s, ld::Atom::ContentType ct, + ld::Atom::SymbolTableInclusion i, bool dds, bool thumb, bool al, + ld::Atom::Alignment a) : ld::Atom(sect, d, c, s, ct, i, dds, thumb, al, a) { } + + virtual void setUUID(const uint8_t digest[16]) = 0; + virtual void recopyUUIDCommand() = 0; +}; + +template <typename A> +class HeaderAndLoadCommandsAtom : public HeaderAndLoadCommandsAbtract +{ +public: + HeaderAndLoadCommandsAtom(const Options& opts, ld::Internal& state, + OutputFile& writer); + + // overrides of ld::Atom + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "mach-o header and load commands"; } + virtual uint64_t size() const; + virtual uint64_t objectAddress() const { return _address; } + virtual void copyRawContent(uint8_t buffer[]) const; + + // overrides of HeaderAndLoadCommandsAbtract + virtual void setUUID(const uint8_t digest[16]) { memcpy(_uuid, digest, 16); } + virtual void recopyUUIDCommand(); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + unsigned int nonHiddenSectionCount() const; + unsigned int segmentCount() const; + static uint32_t alignedSize(uint32_t x); + uint32_t magic() const; + uint32_t cpuType() const; + uint32_t cpuSubType() const; + uint32_t flags() const; + uint32_t fileType() const; + uint32_t commandsCount() const; + uint32_t threadLoadCommandSize() const; + uint8_t* copySingleSegmentLoadCommand(uint8_t* p) const; + uint8_t* copySegmentLoadCommands(uint8_t* p) const; + uint8_t* copyDyldInfoLoadCommand(uint8_t* p) const; + uint8_t* copySymbolTableLoadCommand(uint8_t* p) const; + uint8_t* copyDynamicSymbolTableLoadCommand(uint8_t* p) const; + uint8_t* copyDyldLoadCommand(uint8_t* p) const; + uint8_t* copyDylibIDLoadCommand(uint8_t* p) const; + uint8_t* copyRoutinesLoadCommand(uint8_t* p) const; + uint8_t* copyUUIDLoadCommand(uint8_t* p) const; + uint8_t* copyVersionLoadCommand(uint8_t* p) const; + uint8_t* copyThreadsLoadCommand(uint8_t* p) const; + uint8_t* copyEncryptionLoadCommand(uint8_t* p) const; + uint8_t* copySplitSegInfoLoadCommand(uint8_t* p) const; + uint8_t* copyDylibLoadCommand(uint8_t* p, const ld::dylib::File*) const; + uint8_t* copyRPathLoadCommand(uint8_t* p, const char*) const; + uint8_t* copySubFrameworkLoadCommand(uint8_t* p) const; + uint8_t* copyAllowableClientLoadCommand(uint8_t* p, const char* client) const; + uint8_t* copySubLibraryLoadCommand(uint8_t* p, const char* name) const; + uint8_t* copySubUmbrellaLoadCommand(uint8_t* p, const char* name) const; + uint8_t* copyFunctionStartsLoadCommand(uint8_t* p) const; + uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const; + + uint32_t sectionFlags(ld::Internal::FinalSection* sect) const; + bool sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const; + + + const Options& _options; + ld::Internal& _state; + OutputFile& _writer; + pint_t _address; + bool _hasDyldInfoLoadCommand; + bool _hasDyldLoadCommand; + bool _hasDylibIDLoadCommand; + bool _hasThreadLoadCommand; + bool _hasEncryptionLoadCommand; + bool _hasSplitSegInfoLoadCommand; + bool _hasRoutinesLoadCommand; + bool _hasUUIDLoadCommand; + bool _hasSymbolTableLoadCommand; + bool _hasDynamicSymbolTableLoadCommand; + bool _hasRPathLoadCommands; + bool _hasSubFrameworkLoadCommand; + bool _hasVersionLoadCommand; + bool _hasFunctionStartsLoadCommand; + uint32_t _dylibLoadCommmandsCount; + uint32_t _allowableClientLoadCommmandsCount; + uint32_t _dyldEnvironExrasCount; + std::vector<const char*> _subLibraryNames; + std::vector<const char*> _subUmbrellaNames; + uint8_t _uuid[16]; + mutable macho_uuid_command<P>* _uuidCmdInOutputBuffer; + + static ld::Section _s_section; + static ld::Section _s_preload_section; +}; + +template <typename A> +ld::Section HeaderAndLoadCommandsAtom<A>::_s_section("__TEXT", "__mach_header", ld::Section::typeMachHeader, true); +template <typename A> +ld::Section HeaderAndLoadCommandsAtom<A>::_s_preload_section("__HEADER", "__mach_header", ld::Section::typeMachHeader, true); + + +template <typename A> +HeaderAndLoadCommandsAtom<A>::HeaderAndLoadCommandsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : HeaderAndLoadCommandsAbtract((opts.outputKind() == Options::kPreload) ? _s_preload_section : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableNotIn, false, false, false, + (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(12) ), + _options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL) +{ + bzero(_uuid, 16); + _hasDyldInfoLoadCommand = opts.makeCompressedDyldInfo(); + _hasDyldLoadCommand = ((opts.outputKind() == Options::kDynamicExecutable) || (_options.outputKind() == Options::kDyld)); + _hasDylibIDLoadCommand = (opts.outputKind() == Options::kDynamicLibrary); + _hasThreadLoadCommand = _hasDyldLoadCommand || (opts.outputKind() == Options::kStaticExecutable) + || (opts.outputKind() == Options::kPreload) + || (opts.outputKind() == Options::kDyld); + _hasEncryptionLoadCommand = opts.makeEncryptable(); + _hasSplitSegInfoLoadCommand = opts.sharedRegionEligible(); + _hasRoutinesLoadCommand = (opts.initFunctionName() != NULL); + _hasSymbolTableLoadCommand = true; + _hasUUIDLoadCommand = (opts.UUIDMode() != Options::kUUIDNone); + switch ( opts.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kKextBundle: + _hasDynamicSymbolTableLoadCommand = true; + break; + case Options::kObjectFile: + if ( ! state.someObjectFileHasDwarf ) + _hasUUIDLoadCommand = false; + _hasDynamicSymbolTableLoadCommand = false; + for (std::vector<ld::Internal::FinalSection*>::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( (*it)->type() == ld::Section::typeNonLazyPointer ) { + _hasDynamicSymbolTableLoadCommand = true; + break; + } + } + break; + case Options::kStaticExecutable: + _hasDynamicSymbolTableLoadCommand = false; + break; + case Options::kPreload: + _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); + break; + } + _hasRPathLoadCommands = (_options.rpaths().size() != 0); + _hasSubFrameworkLoadCommand = (_options.umbrellaName() != NULL); + _hasVersionLoadCommand = _options.addVersionLoadCommand(); + _hasFunctionStartsLoadCommand = _options.addFunctionStarts(); + _dylibLoadCommmandsCount = _writer.dylibCount(); + _allowableClientLoadCommmandsCount = _options.allowableClients().size(); + _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); + if ( ! _options.useSimplifiedDylibReExports() ) { + // target OS does not support LC_REEXPORT_DYLIB, so use old complicated load commands + for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + const ld::dylib::File* dylib = _writer.dylibByOrdinal(ord); + if ( dylib->willBeReExported() ) { + // if child says it is an sub-framework of the image being created, then nothing to do here + bool isSubFramework = false; + const char* childInUmbrella = dylib->parentUmbrella(); + if ( childInUmbrella != NULL ) { + const char* myLeaf = strrchr(_options.installPath(), '/'); + if ( myLeaf != NULL ) { + if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 ) + isSubFramework = true; + } + } + // LC_SUB_FRAMEWORK is in child, so do nothing in parent + if ( ! isSubFramework ) { + // this dylib also needs a sub_x load command + bool isFrameworkReExport = false; + const char* lastSlash = strrchr(dylib->installPath(), '/'); + if ( lastSlash != NULL ) { + char frameworkName[strlen(lastSlash)+20]; + sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); + isFrameworkReExport = (strstr(dylib->installPath(), frameworkName) != NULL); + } + if ( isFrameworkReExport ) { + // needs a LC_SUB_UMBRELLA command + _subUmbrellaNames.push_back(&lastSlash[1]); + } + else { + // needs a LC_SUB_LIBRARY command + const char* nameStart = &lastSlash[1]; + if ( lastSlash == NULL ) + nameStart = dylib->installPath(); + int len = strlen(nameStart); + const char* dot = strchr(nameStart, '.'); + if ( dot != NULL ) + len = dot - nameStart; + char* subLibName = new char[len+1]; + strlcpy(subLibName, nameStart, len+1); + _subLibraryNames.push_back(subLibName); + } + } + } + } + } +} + +template <typename A> +uint32_t HeaderAndLoadCommandsAtom<A>::alignedSize(uint32_t size) +{ + if ( sizeof(pint_t) == 4 ) + return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o + else + return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o +} + + +template <typename A> +unsigned int HeaderAndLoadCommandsAtom<A>::nonHiddenSectionCount() const +{ + unsigned int count = 0; + for (std::vector<ld::Internal::FinalSection*>::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( ! (*it)->isSectionHidden() && ((*it)->type() != ld::Section::typeTentativeDefs) ) + ++count; + } + return count; +} + +template <typename A> +unsigned int HeaderAndLoadCommandsAtom<A>::segmentCount() const +{ + if ( _options.outputKind() == Options::kObjectFile ) { + // .o files have one anonymous segment that contains all sections + return 1; + } + + unsigned int count = 0; + const char* lastSegName = ""; + for (std::vector<ld::Internal::FinalSection*>::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( _options.outputKind() == Options::kPreload ) { + if ( (*it)->type() == ld::Section::typeMachHeader ) + continue; // for -preload, don't put hidden __HEADER segment into output + if ( (*it)->type() == ld::Section::typeLinkEdit ) + continue; // for -preload, don't put hidden __LINKEDIT segment into output + } + if ( strcmp(lastSegName, (*it)->segmentName()) != 0 ) { + lastSegName = (*it)->segmentName(); + ++count; + } + } + return count; +} + + +template <typename A> +uint64_t HeaderAndLoadCommandsAtom<A>::size() const +{ + uint32_t sz = sizeof(macho_header<P>); + + sz += sizeof(macho_segment_command<P>) * this->segmentCount(); + sz += sizeof(macho_section<P>) * this->nonHiddenSectionCount(); + + if ( _hasDylibIDLoadCommand ) + sz += alignedSize(sizeof(macho_dylib_command<P>) + strlen(_options.installPath()) + 1); + + if ( _hasDyldInfoLoadCommand ) + sz += sizeof(macho_dyld_info_command<P>); + + if ( _hasSymbolTableLoadCommand ) + sz += sizeof(macho_symtab_command<P>); + + if ( _hasDynamicSymbolTableLoadCommand ) + sz += sizeof(macho_dysymtab_command<P>); + + if ( _hasDyldLoadCommand ) + sz += alignedSize(sizeof(macho_dylinker_command<P>) + strlen(_options.dyldInstallPath()) + 1); + + if ( _hasRoutinesLoadCommand ) + sz += sizeof(macho_routines_command<P>); + + if ( _hasUUIDLoadCommand ) + sz += sizeof(macho_uuid_command<P>); + + if ( _hasVersionLoadCommand ) + sz += sizeof(macho_version_min_command<P>); + + if ( _hasThreadLoadCommand ) + sz += this->threadLoadCommandSize(); + + if ( _hasEncryptionLoadCommand ) + sz += sizeof(macho_encryption_info_command<P>); + + if ( _hasSplitSegInfoLoadCommand ) + sz += sizeof(macho_linkedit_data_command<P>); + + for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + sz += alignedSize(sizeof(macho_dylib_command<P>) + strlen(_writer.dylibByOrdinal(ord)->installPath()) + 1); + } + + if ( _hasRPathLoadCommands ) { + const std::vector<const char*>& rpaths = _options.rpaths(); + for (std::vector<const char*>::const_iterator it = rpaths.begin(); it != rpaths.end(); ++it) { + sz += alignedSize(sizeof(macho_rpath_command<P>) + strlen(*it) + 1); + } + } + + if ( _hasSubFrameworkLoadCommand ) + sz += alignedSize(sizeof(macho_sub_framework_command<P>) + strlen(_options.umbrellaName()) + 1); + + for (std::vector<const char*>::const_iterator it = _subLibraryNames.begin(); it != _subLibraryNames.end(); ++it) { + sz += alignedSize(sizeof(macho_sub_library_command<P>) + strlen(*it) + 1); + } + + for (std::vector<const char*>::const_iterator it = _subUmbrellaNames.begin(); it != _subUmbrellaNames.end(); ++it) { + sz += alignedSize(sizeof(macho_sub_umbrella_command<P>) + strlen(*it) + 1); + } + + if ( _allowableClientLoadCommmandsCount != 0 ) { + const std::vector<const char*>& clients = _options.allowableClients(); + for (std::vector<const char*>::const_iterator it = clients.begin(); it != clients.end(); ++it) { + sz += alignedSize(sizeof(macho_sub_client_command<P>) + strlen(*it) + 1); + } + } + + if ( _dyldEnvironExrasCount != 0 ) { + const std::vector<const char*>& extras = _options.dyldEnvironExtras(); + for (std::vector<const char*>::const_iterator it = extras.begin(); it != extras.end(); ++it) { + sz += alignedSize(sizeof(macho_dylinker_command<P>) + strlen(*it) + 1); + } + } + + if ( _hasFunctionStartsLoadCommand ) + sz += sizeof(macho_linkedit_data_command<P>); + + return sz; +} + +template <typename A> +uint32_t HeaderAndLoadCommandsAtom<A>::commandsCount() const +{ + uint32_t count = this->segmentCount(); + + if ( _hasDylibIDLoadCommand ) + ++count; + + if ( _hasDyldInfoLoadCommand ) + ++count; + + if ( _hasSymbolTableLoadCommand ) + ++count; + + if ( _hasDynamicSymbolTableLoadCommand ) + ++count; + + if ( _hasDyldLoadCommand ) + ++count; + + if ( _hasRoutinesLoadCommand ) + ++count; + + if ( _hasUUIDLoadCommand ) + ++count; + + if ( _hasVersionLoadCommand ) + ++count; + + if ( _hasThreadLoadCommand ) + ++count; + + if ( _hasEncryptionLoadCommand ) + ++count; + + if ( _hasSplitSegInfoLoadCommand ) + ++count; + + count += _dylibLoadCommmandsCount; + + count += _options.rpaths().size(); + + if ( _hasSubFrameworkLoadCommand ) + ++count; + + count += _subLibraryNames.size(); + + count += _subUmbrellaNames.size(); + + count += _allowableClientLoadCommmandsCount; + + count += _dyldEnvironExrasCount; + + if ( _hasFunctionStartsLoadCommand ) + ++count; + + return count; +} + +template <typename A> +uint32_t HeaderAndLoadCommandsAtom<A>::fileType() const +{ + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + return MH_EXECUTE; + case Options::kDynamicLibrary: + return MH_DYLIB; + case Options::kDynamicBundle: + return MH_BUNDLE; + case Options::kObjectFile: + return MH_OBJECT; + case Options::kDyld: + return MH_DYLINKER; + case Options::kPreload: + return MH_PRELOAD; + case Options::kKextBundle: + return MH_KEXT_BUNDLE; + } + throw "unknonwn mach-o file type"; +} + +template <typename A> +uint32_t HeaderAndLoadCommandsAtom<A>::flags() const +{ + uint32_t bits = 0; + if ( _options.outputKind() == Options::kObjectFile ) { + if ( _state.allObjectFilesScatterable ) + bits = MH_SUBSECTIONS_VIA_SYMBOLS; + } + else { + if ( _options.outputKind() == Options::kStaticExecutable ) { + bits |= MH_NOUNDEFS; + } + else if ( _options.outputKind() == Options::kPreload ) { + bits |= MH_NOUNDEFS; + if ( _options.positionIndependentExecutable() ) + bits |= MH_PIE; + } + else { + bits = MH_DYLDLINK; + switch ( _options.nameSpace() ) { + case Options::kTwoLevelNameSpace: + bits |= MH_TWOLEVEL | MH_NOUNDEFS; + break; + case Options::kFlatNameSpace: + break; + case Options::kForceFlatNameSpace: + bits |= MH_FORCE_FLAT; + break; + } + if ( _writer.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols ) + bits |= MH_WEAK_DEFINES; + if ( _writer.usesWeakExternalSymbols || _writer.hasWeakExternalSymbols ) + bits |= MH_BINDS_TO_WEAK; + if ( _options.prebind() ) + bits |= MH_PREBOUND; + if ( _options.splitSeg() ) + bits |= MH_SPLIT_SEGS; + if ( (_options.outputKind() == Options::kDynamicLibrary) + && _writer._noReExportedDylibs + && _options.useSimplifiedDylibReExports() ) { + bits |= MH_NO_REEXPORTED_DYLIBS; + } + if ( _options.positionIndependentExecutable() && ! _writer.pieDisabled ) + bits |= MH_PIE; + if ( _options.markAutoDeadStripDylib() ) + bits |= MH_DEAD_STRIPPABLE_DYLIB; + if ( _writer.hasThreadLocalVariableDefinitions ) + bits |= MH_HAS_TLV_DESCRIPTORS; + if ( _options.hasNonExecutableHeap() ) + bits |= MH_NO_HEAP_EXECUTION; + } + if ( _options.hasExecutableStack() ) + bits |= MH_ALLOW_STACK_EXECUTION; + } + return bits; +} + +template <> uint32_t HeaderAndLoadCommandsAtom<ppc>::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom<ppc64>::magic() const { return MH_MAGIC_64; } +template <> uint32_t HeaderAndLoadCommandsAtom<x86>::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom<x86_64>::magic() const { return MH_MAGIC_64; } +template <> uint32_t HeaderAndLoadCommandsAtom<arm>::magic() const { return MH_MAGIC; } + +template <> uint32_t HeaderAndLoadCommandsAtom<ppc>::cpuType() const { return CPU_TYPE_POWERPC; } +template <> uint32_t HeaderAndLoadCommandsAtom<ppc64>::cpuType() const { return CPU_TYPE_POWERPC64; } +template <> uint32_t HeaderAndLoadCommandsAtom<x86>::cpuType() const { return CPU_TYPE_I386; } +template <> uint32_t HeaderAndLoadCommandsAtom<x86_64>::cpuType() const { return CPU_TYPE_X86_64; } +template <> uint32_t HeaderAndLoadCommandsAtom<arm>::cpuType() const { return CPU_TYPE_ARM; } + + +template <> +uint32_t HeaderAndLoadCommandsAtom<ppc>::cpuSubType() const +{ + return _state.cpuSubType; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<ppc64>::cpuSubType() const +{ + if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) + return (CPU_SUBTYPE_POWERPC_ALL | 0x80000000); + else + return CPU_SUBTYPE_POWERPC_ALL; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<x86>::cpuSubType() const +{ + return CPU_SUBTYPE_I386_ALL; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<x86_64>::cpuSubType() const +{ + if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) + return (CPU_SUBTYPE_X86_64_ALL | 0x80000000); + else + return CPU_SUBTYPE_X86_64_ALL; +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<arm>::cpuSubType() const +{ + return _state.cpuSubType; +} + + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copySingleSegmentLoadCommand(uint8_t* p) const +{ + // in .o files there is just one segment load command with a blank name + // and all sections under it + macho_segment_command<P>* cmd = (macho_segment_command<P>*)p; + cmd->set_cmd(macho_segment_command<P>::CMD); + cmd->set_segname(""); + cmd->set_vmaddr(_options.baseAddress()); + cmd->set_vmsize(0); // updated after sections set + cmd->set_fileoff(0); // updated after sections set + cmd->set_filesize(0); // updated after sections set + cmd->set_maxprot(VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + cmd->set_initprot(VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + cmd->set_nsects(this->nonHiddenSectionCount()); + cmd->set_flags(0); + // add sections array + macho_section<P>* msect = (macho_section<P>*)&p[sizeof(macho_segment_command<P>)]; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* fsect = *sit; + if ( fsect->isSectionHidden() ) + continue; + if ( fsect->type() == ld::Section::typeTentativeDefs ) + continue; + msect->set_sectname(fsect->sectionName()); + msect->set_segname(fsect->segmentName()); + msect->set_addr(fsect->address); + msect->set_size(fsect->size); + msect->set_offset(fsect->fileOffset); + msect->set_align(fsect->alignment); + msect->set_reloff((fsect->relocCount == 0) ? 0 : _writer.sectionRelocationsSection->fileOffset + fsect->relocStart * sizeof(macho_relocation_info<P>)); + msect->set_nreloc(fsect->relocCount); + msect->set_flags(sectionFlags(fsect)); + msect->set_reserved1(fsect->indirectSymTabStartIndex); + msect->set_reserved2(fsect->indirectSymTabElementSize); + // update segment info + if ( cmd->fileoff() == 0 ) + cmd->set_fileoff(fsect->fileOffset); + cmd->set_vmsize(fsect->address + fsect->size - cmd->vmaddr()); + if ( (fsect->type() != ld::Section::typeZeroFill) && (fsect->type() != ld::Section::typeTentativeDefs) ) + cmd->set_filesize(fsect->fileOffset + fsect->size - cmd->fileoff()); + ++msect; + } + cmd->set_cmdsize(sizeof(macho_segment_command<P>) + cmd->nsects()*sizeof(macho_section<P>)); + return p + cmd->cmdsize(); +} + +struct SegInfo { + SegInfo(const char* n, const Options&); + const char* segName; + uint32_t nonHiddenSectionCount; + uint32_t maxProt; + uint32_t initProt; + std::vector<ld::Internal::FinalSection*> sections; +}; + + +SegInfo::SegInfo(const char* n, const Options& opts) + : segName(n), nonHiddenSectionCount(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n)) +{ +} + + +template <typename A> +uint32_t HeaderAndLoadCommandsAtom<A>::sectionFlags(ld::Internal::FinalSection* sect) const +{ + uint32_t bits; + switch ( sect->type() ) { + case ld::Section::typeUnclassified: + if ( strcmp(sect->segmentName(), "__OBJC") == 0 ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (strcmp(sect->sectionName(), "__objc_classlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (strcmp(sect->sectionName(), "__objc_catlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (strncmp(sect->sectionName(), "__objc_superrefs", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else + return S_REGULAR; + case ld::Section::typeCode: + bits = S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; + if ( sect->hasLocalRelocs && ! _writer.pieDisabled ) + bits |= S_ATTR_LOC_RELOC; + if ( sect->hasExternalRelocs ) + bits |= S_ATTR_EXT_RELOC; + return bits; + case ld::Section::typePageZero: + return S_REGULAR; + case ld::Section::typeImportProxies: + return S_REGULAR; + case ld::Section::typeLinkEdit: + return S_REGULAR; + case ld::Section::typeMachHeader: + return S_REGULAR; + case ld::Section::typeStack: + return S_REGULAR; + case ld::Section::typeLiteral4: + return S_4BYTE_LITERALS; + case ld::Section::typeLiteral8: + return S_8BYTE_LITERALS; + case ld::Section::typeLiteral16: + return S_16BYTE_LITERALS; + case ld::Section::typeConstants: + return S_REGULAR; + case ld::Section::typeTempLTO: + assert(0 && "typeTempLTO should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeAbsoluteSymbols: + assert(0 && "typeAbsoluteSymbols should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeCString: + case ld::Section::typeNonStdCString: + return S_CSTRING_LITERALS; + case ld::Section::typeCStringPointer: + return S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP; + case ld::Section::typeUTF16Strings: + return S_REGULAR; + case ld::Section::typeCFString: + return S_REGULAR; + case ld::Section::typeObjC1Classes: + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + case ld::Section::typeCFI: + return S_REGULAR; + case ld::Section::typeLSDA: + return S_REGULAR; + case ld::Section::typeDtraceDOF: + return S_DTRACE_DOF; + case ld::Section::typeUnwindInfo: + return S_REGULAR; + case ld::Section::typeObjCClassRefs: + case ld::Section::typeObjC2CategoryList: + return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + case ld::Section::typeZeroFill: + if ( _options.optimizeZeroFill() ) + return S_ZEROFILL; + else + return S_REGULAR; + case ld::Section::typeTentativeDefs: + assert(0 && "typeTentativeDefs should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeLazyPointer: + case ld::Section::typeLazyPointerClose: + return S_LAZY_SYMBOL_POINTERS; + case ld::Section::typeStubClose: + case ld::Section::typeStub: + if ( sect->hasLocalRelocs ) + return S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_LOC_RELOC; + else + return S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; + case ld::Section::typeNonLazyPointer: + return S_NON_LAZY_SYMBOL_POINTERS; + case ld::Section::typeDyldInfo: + return S_REGULAR; + case ld::Section::typeLazyDylibPointer: + return S_LAZY_DYLIB_SYMBOL_POINTERS; + case ld::Section::typeStubHelper: + if ( sect->hasLocalRelocs ) + return S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_LOC_RELOC; + else + return S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS; + case ld::Section::typeInitializerPointers: + return S_MOD_INIT_FUNC_POINTERS; + case ld::Section::typeTerminatorPointers: + return S_MOD_TERM_FUNC_POINTERS; + case ld::Section::typeTLVInitialValues: + return S_THREAD_LOCAL_REGULAR; + case ld::Section::typeTLVZeroFill: + return S_THREAD_LOCAL_ZEROFILL; + case ld::Section::typeTLVDefs: + return S_THREAD_LOCAL_VARIABLES; + case ld::Section::typeTLVInitializerPointers: + return S_THREAD_LOCAL_INIT_FUNCTION_POINTERS; + case ld::Section::typeTLVPointers: + return S_THREAD_LOCAL_VARIABLE_POINTERS; + case ld::Section::typeFirstSection: + assert(0 && "typeFirstSection should not make it to final linked image"); + return S_REGULAR; + case ld::Section::typeLastSection: + assert(0 && "typeLastSection should not make it to final linked image"); + return S_REGULAR; + } + return S_REGULAR; +} + + +template <typename A> +bool HeaderAndLoadCommandsAtom<A>::sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typeAbsoluteSymbols: + case ld::Section::typeTentativeDefs: + case ld::Section::typeLastSection: + return true; + default: + break; + } + return false; +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copySegmentLoadCommands(uint8_t* p) const +{ + // group sections into segments + std::vector<SegInfo> segs; + const char* lastSegName = ""; + for (std::vector<ld::Internal::FinalSection*>::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( _options.outputKind() == Options::kPreload ) { + if ( (*it)->type() == ld::Section::typeMachHeader ) + continue; // for -preload, don't put hidden __HEADER segment into output + if ( (*it)->type() == ld::Section::typeLinkEdit ) + continue; // for -preload, don't put hidden __LINKEDIT segment into output + } + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + SegInfo si(sect->segmentName(), _options); + segs.push_back(si); + lastSegName = sect->segmentName(); + } + if ( ! sect->isSectionHidden() ) + segs.back().nonHiddenSectionCount++; + segs.back().sections.push_back(sect); + } + // write out segment load commands for each section with trailing sections + for (std::vector<SegInfo>::iterator it = segs.begin(); it != segs.end(); ++it) { + SegInfo& si = *it; + ld::Internal::FinalSection* lastNonZeroFillSection = NULL; + for (int i=si.sections.size()-1; i >= 0; --i) { + if ( !sectionTakesNoDiskSpace(si.sections[i]) ) { + lastNonZeroFillSection = si.sections[i]; + break; + } + } + uint64_t vmsize = si.sections.back()->address + si.sections.back()->size - si.sections.front()->address; + vmsize = ((vmsize+_options.segmentAlignment()-1) & (-_options.segmentAlignment())); + uint64_t filesize = 0; + if ( lastNonZeroFillSection != NULL ) { + filesize = lastNonZeroFillSection->address + lastNonZeroFillSection->size - si.sections.front()->address; + // round up all segments to page aligned, except __LINKEDIT + if ( (si.sections[0]->type() != ld::Section::typeLinkEdit) && (si.sections[0]->type() != ld::Section::typeImportProxies) ) + filesize = (filesize + _options.segmentAlignment()-1) & (-_options.segmentAlignment()); + } + if ( si.sections.front()->type() == ld::Section::typePageZero ) + filesize = 0; + else if ( si.sections.front()->type() == ld::Section::typeStack ) + filesize = 0; + macho_segment_command<P>* segCmd = (macho_segment_command<P>*)p; + segCmd->set_cmd(macho_segment_command<P>::CMD); + segCmd->set_cmdsize(sizeof(macho_segment_command<P>) + si.nonHiddenSectionCount*sizeof(macho_section<P>)); + segCmd->set_segname(si.sections.front()->segmentName()); + segCmd->set_vmaddr(si.sections.front()->address); + segCmd->set_vmsize(vmsize); + segCmd->set_fileoff(si.sections.front()->fileOffset); + segCmd->set_filesize(filesize); + segCmd->set_maxprot(si.maxProt); + segCmd->set_initprot(si.initProt); + segCmd->set_nsects(si.nonHiddenSectionCount); + segCmd->set_flags(0); + p += sizeof(macho_segment_command<P>); + macho_section<P>* msect = (macho_section<P>*)p; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = si.sections.begin(); sit != si.sections.end(); ++sit) { + ld::Internal::FinalSection* fsect = *sit; + if ( ! fsect->isSectionHidden() ) { + msect->set_sectname(fsect->sectionName()); + msect->set_segname(fsect->segmentName()); + msect->set_addr(fsect->address); + msect->set_size(fsect->size); + msect->set_offset(sectionTakesNoDiskSpace(fsect) ? 0 : fsect->fileOffset); + msect->set_align(fsect->alignment); + msect->set_reloff(0); + msect->set_nreloc(0); + msect->set_flags(sectionFlags(fsect)); + msect->set_reserved1(fsect->indirectSymTabStartIndex); + msect->set_reserved2(fsect->indirectSymTabElementSize); + p += sizeof(macho_section<P>); + ++msect; + } + } + } + + return p; +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copySymbolTableLoadCommand(uint8_t* p) const +{ + // build LC_SYMTAB command + macho_symtab_command<P>* symbolTableCmd = (macho_symtab_command<P>*)p; + symbolTableCmd->set_cmd(LC_SYMTAB); + symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>)); + symbolTableCmd->set_nsyms(_writer.symbolTableSection->size/sizeof(macho_nlist<P>)); + symbolTableCmd->set_symoff(_writer.symbolTableSection->size == 0 ? 0 : _writer.symbolTableSection->fileOffset); + symbolTableCmd->set_stroff(_writer.stringPoolSection->size == 0 ? 0 : _writer.stringPoolSection->fileOffset ); + symbolTableCmd->set_strsize(_writer.stringPoolSection->size); + return p + sizeof(macho_symtab_command<P>); +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyDynamicSymbolTableLoadCommand(uint8_t* p) const +{ + // build LC_SYMTAB command + macho_dysymtab_command<P>* dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)p; + dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); + dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>)); + dynamicSymbolTableCmd->set_ilocalsym(0); + dynamicSymbolTableCmd->set_nlocalsym(_writer._localSymbolsCount); + dynamicSymbolTableCmd->set_iextdefsym(dynamicSymbolTableCmd->ilocalsym()+dynamicSymbolTableCmd->nlocalsym()); + dynamicSymbolTableCmd->set_nextdefsym(_writer._globalSymbolsCount); + dynamicSymbolTableCmd->set_iundefsym(dynamicSymbolTableCmd->iextdefsym()+dynamicSymbolTableCmd->nextdefsym()); + dynamicSymbolTableCmd->set_nundefsym(_writer._importSymbolsCount); + + // FIX ME: support for 10.3 dylibs which need modules + //if ( fWriter.fModuleInfoAtom != NULL ) { + // dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset()); + // dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount); + // dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset()); + // dynamicSymbolTableCmd->set_nmodtab(1); + // dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); + // dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); + //} + + bool hasIndirectSymbols = ( (_writer.indirectSymbolTableSection != NULL) && (_writer.indirectSymbolTableSection->size != 0) ); + dynamicSymbolTableCmd->set_indirectsymoff(hasIndirectSymbols ? _writer.indirectSymbolTableSection->fileOffset : 0); + dynamicSymbolTableCmd->set_nindirectsyms( hasIndirectSymbols ? _writer.indirectSymbolTableSection->size/sizeof(uint32_t) : 0); + + // FIX ME: support for classic relocations + if ( _options.outputKind() != Options::kObjectFile ) { + bool hasExternalRelocs = ( (_writer.externalRelocationsSection != NULL) && (_writer.externalRelocationsSection->size != 0) ); + dynamicSymbolTableCmd->set_extreloff(hasExternalRelocs ? _writer.externalRelocationsSection->fileOffset : 0); + dynamicSymbolTableCmd->set_nextrel( hasExternalRelocs ? _writer.externalRelocationsSection->size/8 : 0); + bool hasLocalRelocs = ( (_writer.localRelocationsSection != NULL) && (_writer.localRelocationsSection->size != 0) ); + dynamicSymbolTableCmd->set_locreloff(hasLocalRelocs ? _writer.localRelocationsSection->fileOffset : 0); + dynamicSymbolTableCmd->set_nlocrel (hasLocalRelocs ? _writer.localRelocationsSection->size/8 : 0); + } + return p + sizeof(macho_dysymtab_command<P>); +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyDyldInfoLoadCommand(uint8_t* p) const +{ + // build LC_DYLD_INFO command + macho_dyld_info_command<P>* cmd = (macho_dyld_info_command<P>*)p; + + cmd->set_cmd(LC_DYLD_INFO_ONLY); + cmd->set_cmdsize(sizeof(macho_dyld_info_command<P>)); + if ( _writer.rebaseSection->size != 0 ) { + cmd->set_rebase_off(_writer.rebaseSection->fileOffset); + cmd->set_rebase_size(_writer.rebaseSection->size); + } + if ( _writer.bindingSection->size != 0 ) { + cmd->set_bind_off(_writer.bindingSection->fileOffset); + cmd->set_bind_size(_writer.bindingSection->size); + } + if ( _writer.weakBindingSection->size != 0 ) { + cmd->set_weak_bind_off(_writer.weakBindingSection->fileOffset); + cmd->set_weak_bind_size(_writer.weakBindingSection->size); + } + if ( _writer.lazyBindingSection->size != 0 ) { + cmd->set_lazy_bind_off(_writer.lazyBindingSection->fileOffset); + cmd->set_lazy_bind_size(_writer.lazyBindingSection->size); + } + if ( _writer.exportSection->size != 0 ) { + cmd->set_export_off(_writer.exportSection->fileOffset); + cmd->set_export_size(_writer.exportSection->size); + } + return p + sizeof(macho_dyld_info_command<P>); +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyDyldLoadCommand(uint8_t* p) const +{ + uint32_t sz = alignedSize(sizeof(macho_dylinker_command<P>) + strlen(_options.dyldInstallPath()) + 1); + macho_dylinker_command<P>* cmd = (macho_dylinker_command<P>*)p; + if ( _options.outputKind() == Options::kDyld ) + cmd->set_cmd(LC_ID_DYLINKER); + else + cmd->set_cmd(LC_LOAD_DYLINKER); + cmd->set_cmdsize(sz); + cmd->set_name_offset(); + strcpy((char*)&p[sizeof(macho_dylinker_command<P>)], _options.dyldInstallPath()); + return p + sz; +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyDylibIDLoadCommand(uint8_t* p) const +{ + uint32_t sz = alignedSize(sizeof(macho_dylib_command<P>) + strlen(_options.installPath()) + 1); + macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)p; + cmd->set_cmd(LC_ID_DYLIB); + cmd->set_cmdsize(sz); + cmd->set_name_offset(); + cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses + cmd->set_current_version(_options.currentVersion()); + cmd->set_compatibility_version(_options.compatibilityVersion()); + strcpy((char*)&p[sizeof(macho_dylib_command<P>)], _options.installPath()); + return p + sz; +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyRoutinesLoadCommand(uint8_t* p) const +{ + pint_t initAddr = _state.entryPoint->finalAddress(); + if ( _state.entryPoint->isThumb() ) + initAddr |= 1ULL; + macho_routines_command<P>* cmd = (macho_routines_command<P>*)p; + cmd->set_cmd(macho_routines_command<P>::CMD); + cmd->set_cmdsize(sizeof(macho_routines_command<P>)); + cmd->set_init_address(initAddr); + return p + sizeof(macho_routines_command<P>); +} + + +template <typename A> +void HeaderAndLoadCommandsAtom<A>::recopyUUIDCommand() +{ + assert(_uuidCmdInOutputBuffer != NULL); + _uuidCmdInOutputBuffer->set_uuid(_uuid); +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyUUIDLoadCommand(uint8_t* p) const +{ + macho_uuid_command<P>* cmd = (macho_uuid_command<P>*)p; + cmd->set_cmd(LC_UUID); + cmd->set_cmdsize(sizeof(macho_uuid_command<P>)); + cmd->set_uuid(_uuid); + _uuidCmdInOutputBuffer = cmd; // save for later re-write by recopyUUIDCommand() + return p + sizeof(macho_uuid_command<P>); +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyVersionLoadCommand(uint8_t* p) const +{ + macho_version_min_command<P>* cmd = (macho_version_min_command<P>*)p; + ld::MacVersionMin macVersion = _options.macosxVersionMin(); + ld::IPhoneVersionMin iphoneOSVersion = _options.iphoneOSVersionMin(); + assert( (macVersion != ld::macVersionUnset) || (iphoneOSVersion != ld::iPhoneVersionUnset) ); + if ( macVersion != ld::macVersionUnset ) { + cmd->set_cmd(LC_VERSION_MIN_MACOSX); + cmd->set_cmdsize(sizeof(macho_version_min_command<P>)); + cmd->set_version((uint32_t)macVersion); + cmd->set_reserved(0); + } + else { + cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); + cmd->set_cmdsize(sizeof(macho_version_min_command<P>)); + cmd->set_version((uint32_t)iphoneOSVersion); + cmd->set_reserved(0); + } + return p + sizeof(macho_version_min_command<P>); +} + + +template <> +uint32_t HeaderAndLoadCommandsAtom<ppc>::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 +} + + +template <> +uint8_t* HeaderAndLoadCommandsAtom<ppc>::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command<ppc::P>* cmd = (macho_thread_command<ppc::P>*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(1); // PPC_THREAD_STATE + cmd->set_count(40); // PPC_THREAD_STATE_COUNT; + cmd->set_thread_register(0, start); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(3, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<ppc64>::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom<ppc64>::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command<ppc::P>* cmd = (macho_thread_command<ppc::P>*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(5); // PPC_THREAD_STATE64 + cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; + cmd->set_thread_register(0, start); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(3, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<x86>::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom<x86>::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command<P>* cmd = (macho_thread_command<P>*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(1); // i386_THREAD_STATE + cmd->set_count(16); // i386_THREAD_STATE_COUNT; + cmd->set_thread_register(10, start); + if ( _options.hasCustomStack() ) + cmd->set_thread_register(7, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<x86_64>::threadLoadCommandSize() const +{ + return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom<x86_64>::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command<P>* cmd = (macho_thread_command<P>*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(x86_THREAD_STATE64); + cmd->set_count(x86_THREAD_STATE64_COUNT); + cmd->set_thread_register(16, start); // rip + if ( _options.hasCustomStack() ) + cmd->set_thread_register(7, _options.customStackAddr()); // r1 + return p + threadLoadCommandSize(); +} + +template <> +uint32_t HeaderAndLoadCommandsAtom<arm>::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom<arm>::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + if ( _state.entryPoint->isThumb() ) + start |= 1ULL; + macho_thread_command<P>* cmd = (macho_thread_command<P>*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(1); + cmd->set_count(17); + cmd->set_thread_register(15, start); // pc + if ( _options.hasCustomStack() ) + cmd->set_thread_register(13, _options.customStackAddr()); // sp + return p + threadLoadCommandSize(); +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyEncryptionLoadCommand(uint8_t* p) const +{ + macho_encryption_info_command<P>* cmd = (macho_encryption_info_command<P>*)p; + cmd->set_cmd(LC_ENCRYPTION_INFO); + cmd->set_cmdsize(sizeof(macho_encryption_info_command<P>)); + assert(_writer.encryptedTextStartOffset() != 0); + assert(_writer.encryptedTextEndOffset() != 0); + cmd->set_cryptoff(_writer.encryptedTextStartOffset()); + cmd->set_cryptsize(_writer.encryptedTextEndOffset()-_writer.encryptedTextStartOffset()); + cmd->set_cryptid(0); + return p + sizeof(macho_encryption_info_command<P>); +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copySplitSegInfoLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)p; + cmd->set_cmd(LC_SEGMENT_SPLIT_INFO); + cmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>)); + cmd->set_dataoff(_writer.splitSegInfoSection->fileOffset); + cmd->set_datasize(_writer.splitSegInfoSection->size); + return p + sizeof(macho_linkedit_data_command<P>); +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyDylibLoadCommand(uint8_t* p, const ld::dylib::File* dylib) const +{ + uint32_t sz = alignedSize(sizeof(macho_dylib_command<P>) + strlen(dylib->installPath()) + 1); + macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)p; + // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + bool autoWeakLoadDylib = false; // FIX + //( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0) + //&& (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) ); + if ( dylib->willBeLazyLoadedDylib() ) + cmd->set_cmd(LC_LAZY_LOAD_DYLIB); + else if ( dylib->willBeWeakLinked() || autoWeakLoadDylib ) + cmd->set_cmd(LC_LOAD_WEAK_DYLIB); + else if ( dylib->willBeReExported() && _options.useSimplifiedDylibReExports() ) + cmd->set_cmd(LC_REEXPORT_DYLIB); + else if ( dylib->willBeUpwardDylib() && _options.useUpwardDylibs() ) + cmd->set_cmd(LC_LOAD_UPWARD_DYLIB); + else + cmd->set_cmd(LC_LOAD_DYLIB); + cmd->set_cmdsize(sz); + cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses + cmd->set_current_version(dylib->currentVersion()); + cmd->set_compatibility_version(dylib->compatibilityVersion()); + cmd->set_name_offset(); + strcpy((char*)&p[sizeof(macho_dylib_command<P>)], dylib->installPath()); + return p + sz; +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyRPathLoadCommand(uint8_t* p, const char* path) const +{ + uint32_t sz = alignedSize(sizeof(macho_rpath_command<P>) + strlen(path) + 1); + macho_rpath_command<P>* cmd = (macho_rpath_command<P>*)p; + cmd->set_cmd(LC_RPATH); + cmd->set_cmdsize(sz); + cmd->set_path_offset(); + strcpy((char*)&p[sizeof(macho_rpath_command<P>)], path); + return p + sz; +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copySubFrameworkLoadCommand(uint8_t* p) const +{ + const char* umbrellaName = _options.umbrellaName(); + uint32_t sz = alignedSize(sizeof(macho_sub_framework_command<P>) + strlen(umbrellaName) + 1); + macho_sub_framework_command<P>* cmd = (macho_sub_framework_command<P>*)p; + cmd->set_cmd(LC_SUB_FRAMEWORK); + cmd->set_cmdsize(sz); + cmd->set_umbrella_offset(); + strcpy((char*)&p[sizeof(macho_sub_framework_command<P>)], umbrellaName); + return p + sz; +} + + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyAllowableClientLoadCommand(uint8_t* p, const char* client) const +{ + uint32_t sz = alignedSize(sizeof(macho_sub_client_command<P>) + strlen(client) + 1); + macho_sub_client_command<P>* cmd = (macho_sub_client_command<P>*)p; + cmd->set_cmd(LC_SUB_CLIENT); + cmd->set_cmdsize(sz); + cmd->set_client_offset(); + strcpy((char*)&p[sizeof(macho_sub_client_command<P>)], client); + return p + sz; +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyDyldEnvLoadCommand(uint8_t* p, const char* env) const +{ + uint32_t sz = alignedSize(sizeof(macho_dylinker_command<P>) + strlen(env) + 1); + macho_dylinker_command<P>* cmd = (macho_dylinker_command<P>*)p; + cmd->set_cmd(LC_DYLD_ENVIRONMENT); + cmd->set_cmdsize(sz); + cmd->set_name_offset(); + strcpy((char*)&p[sizeof(macho_dylinker_command<P>)], env); + return p + sz; +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copySubUmbrellaLoadCommand(uint8_t* p, const char* nm) const +{ + uint32_t sz = alignedSize(sizeof(macho_sub_umbrella_command<P>) + strlen(nm) + 1); + macho_sub_umbrella_command<P>* cmd = (macho_sub_umbrella_command<P>*)p; + cmd->set_cmd(LC_SUB_UMBRELLA); + cmd->set_cmdsize(sz); + cmd->set_sub_umbrella_offset(); + strcpy((char*)&p[sizeof(macho_sub_umbrella_command<P>)], nm); + return p + sz; +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copySubLibraryLoadCommand(uint8_t* p, const char* nm) const +{ + uint32_t sz = alignedSize(sizeof(macho_sub_library_command<P>) + strlen(nm) + 1); + macho_sub_library_command<P>* cmd = (macho_sub_library_command<P>*)p; + cmd->set_cmd(LC_SUB_LIBRARY); + cmd->set_cmdsize(sz); + cmd->set_sub_library_offset(); + strcpy((char*)&p[sizeof(macho_sub_library_command<P>)], nm); + return p + sz; +} + +template <typename A> +uint8_t* HeaderAndLoadCommandsAtom<A>::copyFunctionStartsLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)p; + cmd->set_cmd(LC_FUNCTION_STARTS); + cmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>)); + cmd->set_dataoff(_writer.functionStartsSection->fileOffset); + cmd->set_datasize(_writer.functionStartsSection->size); + return p + sizeof(macho_linkedit_data_command<P>); +} + + +template <typename A> +void HeaderAndLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const +{ + macho_header<P>* mh = (macho_header<P>*)buffer; + bzero(buffer, this->size()); + + // copy mach_header + mh->set_magic(this->magic()); + mh->set_cputype(this->cpuType()); + mh->set_cpusubtype(this->cpuSubType()); + mh->set_filetype(this->fileType()); + mh->set_ncmds(this->commandsCount()); + mh->set_sizeofcmds(this->size()-sizeof(macho_header<P>)); + mh->set_flags(this->flags()); + + // copy load commands + uint8_t* p = &buffer[sizeof(macho_header<P>)]; + + if ( _options.outputKind() == Options::kObjectFile ) + p = this->copySingleSegmentLoadCommand(p); + else + p = this->copySegmentLoadCommands(p); + + if ( _hasDylibIDLoadCommand ) + p = this->copyDylibIDLoadCommand(p); + + if ( _hasDyldInfoLoadCommand ) + p = this->copyDyldInfoLoadCommand(p); + + if ( _hasSymbolTableLoadCommand ) + p = this->copySymbolTableLoadCommand(p); + + if ( _hasDynamicSymbolTableLoadCommand ) + p = this->copyDynamicSymbolTableLoadCommand(p); + + if ( _hasDyldLoadCommand ) + p = this->copyDyldLoadCommand(p); + + if ( _hasRoutinesLoadCommand ) + p = this->copyRoutinesLoadCommand(p); + + if ( _hasUUIDLoadCommand ) + p = this->copyUUIDLoadCommand(p); + + if ( _hasVersionLoadCommand ) + p = this->copyVersionLoadCommand(p); + + if ( _hasThreadLoadCommand ) + p = this->copyThreadsLoadCommand(p); + + if ( _hasEncryptionLoadCommand ) + p = this->copyEncryptionLoadCommand(p); + + if ( _hasSplitSegInfoLoadCommand ) + p = this->copySplitSegInfoLoadCommand(p); + + for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + p = this->copyDylibLoadCommand(p, _writer.dylibByOrdinal(ord)); + } + + if ( _hasRPathLoadCommands ) { + const std::vector<const char*>& rpaths = _options.rpaths(); + for (std::vector<const char*>::const_iterator it = rpaths.begin(); it != rpaths.end(); ++it) { + p = this->copyRPathLoadCommand(p, *it); + } + } + + if ( _hasSubFrameworkLoadCommand ) + p = this->copySubFrameworkLoadCommand(p); + + for (std::vector<const char*>::const_iterator it = _subLibraryNames.begin(); it != _subLibraryNames.end(); ++it) { + p = this->copySubLibraryLoadCommand(p, *it); + } + + for (std::vector<const char*>::const_iterator it = _subUmbrellaNames.begin(); it != _subUmbrellaNames.end(); ++it) { + p = this->copySubUmbrellaLoadCommand(p, *it); + } + + if ( _allowableClientLoadCommmandsCount != 0 ) { + const std::vector<const char*>& clients = _options.allowableClients(); + for (std::vector<const char*>::const_iterator it = clients.begin(); it != clients.end(); ++it) { + p = this->copyAllowableClientLoadCommand(p, *it); + } + } + + if ( _dyldEnvironExrasCount != 0 ) { + const std::vector<const char*>& extras = _options.dyldEnvironExtras(); + for (std::vector<const char*>::const_iterator it = extras.begin(); it != extras.end(); ++it) { + p = this->copyDyldEnvLoadCommand(p, *it); + } + } + + if ( _hasFunctionStartsLoadCommand ) + p = this->copyFunctionStartsLoadCommand(p); + +} + + + +} // namespace tool +} // namespace ld + +#endif // __HEADER_LOAD_COMMANDS_HPP__ diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp new file mode 100644 index 0000000..bc292bb --- /dev/null +++ b/src/ld/InputFiles.cpp @@ -0,0 +1,943 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <mach/mach_time.h> +#include <mach/vm_statistics.h> +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> +#include <mach-o/fat.h> + +#include <string> +#include <map> +#include <set> +#include <string> +#include <vector> +#include <list> +#include <algorithm> +#include <ext/hash_map> +#include <ext/hash_set> +#include <dlfcn.h> +#include <AvailabilityMacros.h> + +#include "Options.h" + +#include "InputFiles.h" +#include "macho_relocatable_file.h" +#include "macho_dylib_file.h" +#include "archive_file.h" +#include "lto_file.h" +#include "opaque_section_file.h" + + +namespace ld { +namespace tool { + + + + +class DSOHandleAtom : public ld::Atom { +public: + DSOHandleAtom(const char* nm, ld::Atom::Scope sc, + ld::Atom::SymbolTableInclusion inc, bool preload=false) + : ld::Atom(preload ? _s_section_preload : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + sc, ld::Atom::typeUnclassified, inc, true, false, false, + ld::Atom::Alignment(1)), _name(nm) {} + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { } + virtual void setScope(Scope) { } + + virtual ~DSOHandleAtom() {} + + static ld::Section _s_section; + static ld::Section _s_section_preload; + static DSOHandleAtom _s_atomAll; + static DSOHandleAtom _s_atomExecutable; + static DSOHandleAtom _s_atomDylib; + static DSOHandleAtom _s_atomBundle; + static DSOHandleAtom _s_atomDyld; + static DSOHandleAtom _s_atomObjectFile; + static DSOHandleAtom _s_atomPreload; +private: + const char* _name; +}; +ld::Section DSOHandleAtom::_s_section("__TEXT", "__mach_header", ld::Section::typeMachHeader, true); +ld::Section DSOHandleAtom::_s_section_preload("__HEADER", "__mach_header", ld::Section::typeMachHeader, true); +DSOHandleAtom DSOHandleAtom::_s_atomAll("___dso_handle", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomExecutable("__mh_execute_header", ld::Atom::scopeGlobal, ld::Atom::symbolTableInAndNeverStrip); +DSOHandleAtom DSOHandleAtom::_s_atomDylib("__mh_dylib_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomBundle("__mh_bundle_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomDyld("__mh_dylinker_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomObjectFile("__mh_object_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn); +DSOHandleAtom DSOHandleAtom::_s_atomPreload("__mh_preload_header", ld::Atom::scopeLinkageUnit, ld::Atom::symbolTableNotIn, true); + + + +class PageZeroAtom : public ld::Atom { +public: + PageZeroAtom(uint64_t sz) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeZeroFill, + symbolTableNotIn, true, false, false, ld::Atom::Alignment(12)), + _size(sz) {} + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "page zero"; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { } + virtual void setScope(Scope) { } + + virtual ~PageZeroAtom() {} + + static ld::Section _s_section; + static DSOHandleAtom _s_atomAll; +private: + uint64_t _size; +}; +ld::Section PageZeroAtom::_s_section("__PAGEZERO", "__pagezero", ld::Section::typePageZero, true); + + +class CustomStackAtom : public ld::Atom { +public: + CustomStackAtom(uint64_t sz) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeZeroFill, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(12)), + _size(sz) {} + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "custom stack"; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { } + virtual void setScope(Scope) { } + + virtual ~CustomStackAtom() {} + +private: + uint64_t _size; + static ld::Section _s_section; +}; +ld::Section CustomStackAtom::_s_section("__UNIXSTACK", "__stack", ld::Section::typeStack, true); + + + +const char* InputFiles::fileArch(const uint8_t* p, unsigned len) +{ + const char* result = mach_o::relocatable::archName(p); + if ( result != NULL ) + return result; + + result = lto::archName(p, len); + if ( result != NULL ) + return result; + + if ( strncmp((const char*)p, "!<arch>\n", 8) == 0 ) + return "archive"; + + return "unsupported file format"; +} + + +ld::File* InputFiles::makeFile(const Options::FileInfo& info) +{ + // map in whole file + uint64_t len = info.fileLen; + int fd = ::open(info.path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + if ( info.fileLen < 20 ) + throw "file too small"; + + uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file, errno=%d", errno); + + // if fat file, skip to architecture we want + // Note: fat header is always big-endian + bool isFatFile = false; + const fat_header* fh = (fat_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + isFatFile = true; + const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); + uint32_t sliceToUse; + bool sliceFound = false; + if ( _options.preferSubArchitecture() ) { + // first try to find a slice that match cpu-type and cpu-sub-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture()) + && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)_options.subArchitecture()) ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( !sliceFound ) { + // look for any slice that matches just cpu-type + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)_options.architecture() ) { + sliceToUse = i; + sliceFound = true; + break; + } + } + } + if ( sliceFound ) { + uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); + len = OSSwapBigToHostInt32(archs[sliceToUse].size); + if ( fileOffset+len > info.fileLen ) { + throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu", + fileOffset, fileOffset+len, info.fileLen); + } + // if requested architecture is page aligned within fat file, then remap just that portion of file + if ( (fileOffset & 0x00000FFF) == 0 ) { + // unmap whole file + munmap((caddr_t)p, info.fileLen); + // re-map just part we need + p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); + if ( p == (uint8_t*)(-1) ) + throwf("can't re-map file, errno=%d", errno); + } + else { + p = &p[fileOffset]; + } + } + } + ::close(fd); + + // see if it is an object file + mach_o::relocatable::ParserOptions objOpts; + objOpts.architecture = _options.architecture(); + objOpts.objSubtypeMustMatch = _options.preferSubArchitecture(); + objOpts.logAllFiles = _options.logAllFiles(); + objOpts.convertUnwindInfo = _options.needsUnwindInfoSection(); + objOpts.subType = _options.subArchitecture(); + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, _nextInputOrdinal, objOpts); + if ( objResult != NULL ) + return this->addObject(objResult, info, len); + + // see if it is an llvm object file + objResult = lto::parse(p, len, info.path, info.modTime, _nextInputOrdinal, _options.architecture(), _options.subArchitecture(), _options.logAllFiles()); + if ( objResult != NULL ) + return this->addObject(objResult, info, len); + + // see if it is a dynamic library + ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, _nextInputOrdinal, info.options.fBundleLoader); + if ( dylibResult != NULL ) + return this->addDylib(dylibResult, info, len); + + // see if it is a static library + archive::ParserOptions archOpts; + archOpts.objOpts = objOpts; + archOpts.forceLoadThisArchive = info.options.fForceLoad; + archOpts.forceLoadAll = _options.fullyLoadArchives(); + archOpts.forceLoadObjC = _options.loadAllObjcObjectsFromArchives(); + archOpts.verboseLoad = _options.whyLoad(); + archOpts.logAllFiles = _options.logAllFiles(); + ld::File* archiveResult = archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts); + if ( archiveResult != NULL ) + return this->addArchive(archiveResult, info, len); + + // does not seem to be any valid linker input file, check LTO misconfiguration problems + if ( lto::archName((uint8_t*)p, len) != NULL ) { + if ( lto::libLTOisLoaded() ) { + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName()); + } + else { + const char* libLTO = "libLTO.dylib"; + char ldPath[PATH_MAX]; + char tmpPath[PATH_MAX]; + char libLTOPath[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( realpath(ldPath, tmpPath) != NULL ) { + char* lastSlash = strrchr(tmpPath, '/'); + if ( lastSlash != NULL ) + strcpy(lastSlash, "/../lib/libLTO.dylib"); + libLTO = tmpPath; + if ( realpath(tmpPath, libLTOPath) != NULL ) + libLTO = libLTOPath; + } + } + throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO); + } + } + + // error handling + if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + throwf("missing required architecture %s in file", _options.architectureName()); + } + else { + if ( isFatFile ) + throwf("file is universal but does not contain a(n) %s slice", _options.architectureName()); + else + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p, len), _options.architectureName()); + } +} + +void InputFiles::logDylib(ld::File* file, bool indirect) +{ + if ( _options.traceDylibs() ) { + const char* fullPath = file->path(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(file); + if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) { + // don't log upward dylibs when XBS is computing dependencies + logTraceInfo("[Logging for XBS] Used upward dynamic library: %s\n", fullPath); + } + else { + if ( indirect ) + logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); + else + logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); + } + } +} + +void InputFiles::logArchive(ld::File* file) const +{ + if ( _options.traceArchives() && (_archiveFilesLogged.count(file) == 0) ) { + // <rdar://problem/4947347> LD_TRACE_ARCHIVES should only print out when a .o is actually used from an archive + _archiveFilesLogged.insert(file); + const char* fullPath = file->path(); + char realName[MAXPATHLEN]; + if ( realpath(fullPath, realName) != NULL ) + fullPath = realName; + logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); + } +} + + +void InputFiles::logTraceInfo(const char* format, ...) const +{ + // one time open() of custom LD_TRACE_FILE + static int trace_file = -1; + if ( trace_file == -1 ) { + const char *trace_file_path = _options.traceOutputFile(); + if ( trace_file_path != NULL ) { + trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); + if ( trace_file == -1 ) + throwf("Could not open or create trace file: %s", trace_file_path); + } + else { + trace_file = fileno(stderr); + } + } + + char trace_buffer[MAXPATHLEN * 2]; + va_list ap; + va_start(ap, format); + int length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); + va_end(ap); + char* buffer_ptr = trace_buffer; + + while (length > 0) { + ssize_t amount_written = write(trace_file, buffer_ptr, length); + if(amount_written == -1) + /* Failure to write shouldn't fail the build. */ + return; + buffer_ptr += amount_written; + length -= amount_written; + } +} + +ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath) +{ + //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); + InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath); + if ( pos != _installPathToDylibs.end() ) { + return pos->second; + } + else { + // allow -dylib_path option to override indirect library to use + for (std::vector<Options::DylibOverride>::const_iterator dit = _options.dylibOverrides().begin(); dit != _options.dylibOverrides().end(); ++dit) { + if ( strcmp(dit->installName,installPath) == 0 ) { + try { + Options::FileInfo info = _options.findFile(dit->useInstead); + ld::File* reader = this->makeFile(info); + ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader); + if ( dylibReader != NULL ) { + //_installPathToDylibs[strdup(installPath)] = dylibReader; + this->logDylib(dylibReader, true); + return dylibReader; + } + else + throwf("indirect dylib at %s is not a dylib", dit->useInstead); + } + catch (const char* msg) { + warning("ignoring -dylib_file option, %s", msg); + } + } + } + char newPath[MAXPATHLEN]; + // handle @loader_path + if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { + strcpy(newPath, fromPath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &installPath[13]); + else + strcpy(newPath, &installPath[13]); + installPath = newPath; + } + // note: @executable_path case is handled inside findFileUsingPaths() + // search for dylib using -F and -L paths + Options::FileInfo info = _options.findFileUsingPaths(installPath); + try { + ld::File* reader = this->makeFile(info); + ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader); + if ( dylibReader != NULL ) { + //assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end()); + //_installPathToDylibs[strdup(installPath)] = dylibReader; + this->logDylib(dylibReader, true); + return dylibReader; + } + else + throwf("indirect dylib at %s is not a dylib", info.path); + } + catch (const char* msg) { + throwf("in %s, %s", info.path, msg); + } + } +} + + + +void InputFiles::createIndirectDylibs() +{ + _allDirectDylibsLoaded = true; + + // mark all dylibs initially specified as required and check if they can be used + for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) { + it->second->setExplicitlyLinked(); + this->checkDylibClientRestrictions(it->second); + } + + // keep processing dylibs until no more dylibs are added + unsigned long lastMapSize = 0; + std::set<ld::dylib::File*> dylibsProcessed; + while ( lastMapSize != _allDylibs.size() ) { + lastMapSize = _allDylibs.size(); + // can't iterator _installPathToDylibs while modifying it, so use temp buffer + std::vector<ld::dylib::File*> unprocessedDylibs; + for (std::set<ld::dylib::File*>::iterator it=_allDylibs.begin(); it != _allDylibs.end(); it++) { + if ( dylibsProcessed.count(*it) == 0 ) + unprocessedDylibs.push_back(*it); + } + for (std::vector<ld::dylib::File*>::iterator it=unprocessedDylibs.begin(); it != unprocessedDylibs.end(); it++) { + dylibsProcessed.insert(*it); + (*it)->processIndirectLibraries(this, _options.implicitlyLinkIndirectPublicDylibs()); + } + } + + // go back over original dylibs and mark sub frameworks as re-exported + if ( _options.outputKind() == Options::kDynamicLibrary ) { + const char* myLeaf = strrchr(_options.installPath(), '/'); + if ( myLeaf != NULL ) { + for (std::vector<class ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); it++) { + ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(*it); + if ( dylibReader != NULL ) { + const char* childParent = dylibReader->parentUmbrella(); + if ( childParent != NULL ) { + if ( strcmp(childParent, &myLeaf[1]) == 0 ) { + // mark that this dylib will be re-exported + dylibReader->setWillBeReExported(); + } + } + } + } + } + } + +} + +void InputFiles::createOpaqueFileSections() +{ + // extra command line section always at end + for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) { + _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, + it->dataLen, _nextInputOrdinal)); + // bump ordinal + _nextInputOrdinal++; + } + +} + + +void InputFiles::checkDylibClientRestrictions(ld::dylib::File* dylib) +{ + // Check for any restrictions on who can link with this dylib + const char* dylibParentName = dylib->parentUmbrella() ; + const std::vector<const char*>* clients = dylib->allowableClients(); + if ( (dylibParentName != NULL) || (clients != NULL) ) { + // only dylibs that are in an umbrella or have a client list need verification + const char* installName = _options.installPath(); + const char* installNameLastSlash = strrchr(installName, '/'); + bool isParent = false; + bool isSibling = false; + bool isAllowableClient = false; + // There are three cases: + if ( (dylibParentName != NULL) && (installNameLastSlash != NULL) ) { + // starts after last slash + const char* myName = &installNameLastSlash[1]; + unsigned int myNameLen = strlen(myName); + if ( strncmp(myName, "lib", 3) == 0 ) + myName = &myName[3]; + // up to first dot + const char* firstDot = strchr(myName, '.'); + if ( firstDot != NULL ) + myNameLen = firstDot - myName; + // up to first underscore + const char* firstUnderscore = strchr(myName, '_'); + if ( (firstUnderscore != NULL) && ((firstUnderscore - myName) < (int)myNameLen) ) + myNameLen = firstUnderscore - myName; + + // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella + isParent = ( (strlen(dylibParentName) == myNameLen) && (strncmp(myName, dylibParentName, myNameLen) == 0) ); + + // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent + isSibling = ( (_options.umbrellaName() != NULL) && (strcmp(_options.umbrellaName(), dylibParentName) == 0) ); + } + + if ( !isParent && !isSibling && (clients != NULL) ) { + // case 3) the dylib has a list of allowable clients, and we are creating one of them + const char* clientName = _options.clientName(); + int clientNameLen = 0; + if ( clientName != NULL ) { + // use client name as specified on command line + clientNameLen = strlen(clientName); + } + else { + // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) + clientName = installName; + clientNameLen = strlen(clientName); + // starts after last slash + if ( installNameLastSlash != NULL ) + clientName = &installNameLastSlash[1]; + if ( strncmp(clientName, "lib", 3) == 0 ) + clientName = &clientName[3]; + // up to first dot + const char* firstDot = strchr(clientName, '.'); + if ( firstDot != NULL ) + clientNameLen = firstDot - clientName; + // up to first underscore + const char* firstUnderscore = strchr(clientName, '_'); + if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) + clientNameLen = firstUnderscore - clientName; + } + + // Use clientName to check if this dylib is able to link against the allowable clients. + for (std::vector<const char*>::const_iterator it = clients->begin(); it != clients->end(); it++) { + if ( strncmp(*it, clientName, clientNameLen) == 0 ) + isAllowableClient = true; + } + } + + if ( !isParent && !isSibling && !isAllowableClient ) { + if ( dylibParentName != NULL ) { + throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", + dylib->path(), dylibParentName); + } + else { + throwf("cannot link directly with %s", dylib->path()); + } + } + } +} + + +void InputFiles::inferArchitecture(Options& opts, const char** archName) +{ + _inferredArch = true; + // scan all input files, looking for a thin .o file. + // the first one found is presumably the architecture to link + uint8_t buffer[sizeof(mach_header_64)]; + const std::vector<Options::FileInfo>& files = opts.getInputFiles(); + for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) { + int fd = ::open(it->path, O_RDONLY, 0); + if ( fd != -1 ) { + ssize_t amount = read(fd, buffer, sizeof(buffer)); + ::close(fd); + if ( amount >= (ssize_t)sizeof(buffer) ) { + cpu_type_t type; + cpu_subtype_t subtype; + if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype) ) { + opts.setArchitecture(type, subtype); + *archName = opts.architectureName(); + return; + } + } + } + } + + // no thin .o files found, so default to same architecture this tool was built as + warning("-arch not specified"); +#if __ppc__ + opts.setArchitecture(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL); +#elif __i386__ + opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL); +#elif __ppc64__ + opts.setArchitecture(CPU_TYPE_POWERPC64, CPU_SUBTYPE_POWERPC_ALL); +#elif __x86_64__ + opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL); +#elif __arm__ + opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6); +#else + #error unknown default architecture +#endif + *archName = opts.architectureName(); +} + + +InputFiles::InputFiles(Options& opts, const char** archName) + : _totalObjectSize(0), _totalArchiveSize(0), + _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0), + _options(opts), _bundleLoader(NULL), _nextInputOrdinal(1), + _allDirectDylibsLoaded(false), _inferredArch(false) +{ +// fStartCreateReadersTime = mach_absolute_time(); + if ( opts.architecture() == 0 ) { + // command line missing -arch, so guess arch + inferArchitecture(opts, archName); + } + + const std::vector<Options::FileInfo>& files = opts.getInputFiles(); + if ( files.size() == 0 ) + throw "no object files specified"; + // add all direct object, archives, and dylibs + _inputFiles.reserve(files.size()); + for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) { + const Options::FileInfo& entry = *it; + try { + _inputFiles.push_back(this->makeFile(entry)); + } + catch (const char* msg) { + if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { + if ( opts.ignoreOtherArchInputFiles() ) { + // ignore, because this is about an architecture not in use + } + else { + warning("ignoring file %s, %s", entry.path, msg); + } + } + else { + throwf("in %s, %s", entry.path, msg); + } + } + } + + this->createIndirectDylibs(); + this->createOpaqueFileSections(); +} + + + +ld::File* InputFiles::addArchive(ld::File* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + // bump ordinal + _nextInputOrdinal += reader->subFileCount(); + + // update stats + _totalArchiveSize += mappedLen; + _totalArchivesLoaded++; + return reader; +} + + +ld::File* InputFiles::addObject(ld::relocatable::File* file, const Options::FileInfo& info, uint64_t mappedLen) +{ + // bump ordinal + _nextInputOrdinal++; + + // update stats + _totalObjectSize += mappedLen; + _totalObjectLoaded++; + return file; +} + + +ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& info, uint64_t mappedLen) +{ + _allDylibs.insert(reader); + + if ( (reader->installPath() == NULL) && !info.options.fBundleLoader ) { + // this is a "blank" stub + // silently ignore it + return reader; + } + // store options about how dylib will be used in dylib itself + if ( info.options.fWeakImport ) + reader->setWillBeWeakLinked(); + if ( info.options.fReExport ) + reader->setWillBeReExported(); + if ( info.options.fUpward ) { + if ( _options.outputKind() == Options::kDynamicLibrary ) + reader->setWillBeUpwardDylib(); + else + warning("ignoring upward dylib option for %s\n", info.path); + } + if ( info.options.fLazyLoad ) + reader->setWillBeLazyLoadedDylb(); + + // add to map of loaded dylibs + const char* installPath = reader->installPath(); + if ( installPath != NULL ) { + InstallNameToDylib::iterator pos = _installPathToDylibs.find(installPath); + if ( pos == _installPathToDylibs.end() ) { + _installPathToDylibs[strdup(installPath)] = reader; + } + else { + bool dylibOnCommandLineTwice = ( strcmp(pos->second->path(), reader->path()) == 0 ); + bool isSymlink = false; + // ignore if this is a symlink to a dylib we've already loaded + if ( !dylibOnCommandLineTwice ) { + char existingDylibPath[PATH_MAX]; + if ( realpath(pos->second->path(), existingDylibPath) != NULL ) { + char newDylibPath[PATH_MAX]; + if ( realpath(reader->path(), newDylibPath) != NULL ) { + isSymlink = ( strcmp(existingDylibPath, newDylibPath) == 0 ); + } + } + } + if ( !dylibOnCommandLineTwice && !isSymlink ) + warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); + } + } + else if ( info.options.fBundleLoader ) + _bundleLoader = reader; + + // log direct readers + if ( !_allDirectDylibsLoaded ) + this->logDylib(reader, false); + + // bump ordinal + _nextInputOrdinal++; + + // update stats + _totalDylibsLoaded++; + + return reader; +} + + +bool InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) const +{ + bool didSomething = false; + for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { + didSomething |= (*it)->forEachAtom(handler); + } + if ( didSomething || true ) { + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomExecutable); + handler.doAtom(DSOHandleAtom::_s_atomAll); + if ( _options.pageZeroSize() != 0 ) + handler.doAtom(*new PageZeroAtom(_options.pageZeroSize())); + if ( _options.hasCustomStack() ) + handler.doAtom(*new CustomStackAtom(_options.customStackSize())); + break; + case Options::kDynamicLibrary: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomDylib); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kDynamicBundle: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomBundle); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kDyld: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomDyld); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kStaticExecutable: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomExecutable); + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + case Options::kPreload: + // add implicit __mh_preload_header label + handler.doAtom(DSOHandleAtom::_s_atomPreload); + break; + case Options::kObjectFile: + handler.doAtom(DSOHandleAtom::_s_atomObjectFile); + break; + case Options::kKextBundle: + // add implicit __dso_handle label + handler.doAtom(DSOHandleAtom::_s_atomAll); + break; + } + } + return didSomething; +} + + +bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler& handler) const +{ + // check each input file + for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { + ld::File* file = *it; + // if this reader is a static archive that has the symbol we need, pull in all atoms in that module + // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. + ld::dylib::File* dylibFile = dynamic_cast<ld::dylib::File*>(file); + if ( searchDylibs && (dylibFile != NULL) ) { + //fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() ); + if ( dylibFile->justInTimeforEachAtom(name, handler) ) { + // we found a definition in this dylib + // done, unless it is a weak definition in which case we keep searching + if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) + return true; + // else continue search for a non-weak definition + } + } + else if ( searchArchives && (dylibFile == NULL) ) { + if ( file->justInTimeforEachAtom(name, handler) ) { + if ( _options.traceArchives() ) + logArchive(file); + // found definition in static library, done + return true; + } + } + } + + // search indirect dylibs + if ( searchDylibs ) { + for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { + ld::dylib::File* dylibFile = it->second; + bool searchThisDylib = false; + if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) { + // for two level namesapce, just check all implicitly linked dylibs + searchThisDylib = dylibFile->implicitlyLinked() && !dylibFile->explicitlyLinked(); + } + else { + // for flat namespace, check all indirect dylibs + searchThisDylib = ! dylibFile->explicitlyLinked(); + } + if ( searchThisDylib ) { + //fprintf(stderr, "searchLibraries(%s), looking in implicitly linked %s\n", name, dylibFile->path() ); + if ( dylibFile->justInTimeforEachAtom(name, handler) ) { + // we found a definition in this dylib + // done, unless it is a weak definition in which case we keep searching + if ( !dylibFile->hasWeakExternals() || !dylibFile->hasWeakDefinition(name)) + return true; + // else continue search for a non-weak definition + } + } + } + } + + return false; +} + + +bool InputFiles::searchWeakDefInDylib(const char* name) const +{ + // search all relevant dylibs to see if any of a weak-def with this name + for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { + ld::dylib::File* dylibFile = it->second; + if ( dylibFile->implicitlyLinked() || dylibFile->explicitlyLinked() ) { + if ( dylibFile->hasWeakExternals() && dylibFile->hasWeakDefinition(name) ) { + return true; + } + } + } + return false; +} + +void InputFiles::dylibs(ld::Internal& state) +{ + bool dylibsOK; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + dylibsOK = true; + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kObjectFile: + case Options::kKextBundle: + dylibsOK = false; + break; + } + + // add command line dylibs in order + for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) { + ld::dylib::File* dylibFile = dynamic_cast<ld::dylib::File*>(*it); + // only add dylibs that are not "blank" dylib stubs + if ( (dylibFile != NULL) && ((dylibFile->installPath() != NULL) || (dylibFile == _bundleLoader)) ) { + if ( dylibsOK ) + state.dylibs.push_back(dylibFile); + else + warning("unexpected dylib (%s) on link line", dylibFile->path()); + } + } + // add implicitly linked dylibs + if ( _options.nameSpace() == Options::kTwoLevelNameSpace ) { + for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { + ld::dylib::File* dylibFile = it->second; + if ( dylibFile->implicitlyLinked() && dylibsOK ) + state.dylibs.push_back(dylibFile); + } + } + // and -bundle_loader + state.bundleLoader = _bundleLoader; +} + + +} // namespace tool +} // namespace ld + diff --git a/src/ld/InputFiles.h b/src/ld/InputFiles.h new file mode 100644 index 0000000..1565515 --- /dev/null +++ b/src/ld/InputFiles.h @@ -0,0 +1,115 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __INPUT_FILES_H__ +#define __INPUT_FILES_H__ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <mach/mach_time.h> +#include <mach/vm_statistics.h> +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> + +#include <vector> + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + +class InputFiles : public ld::dylib::File::DylibHandler +{ +public: + InputFiles(Options& opts, const char** archName); + + // implementation from ld::dylib::File::DylibHandler + virtual ld::dylib::File* findDylib(const char* installPath, const char* fromPath); + + // iterates all atoms in initial files + bool forEachInitialAtom(ld::File::AtomHandler&) const; + // searches libraries for name + bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler&) const; + // see if any linked dylibs export a weak def of symbol + bool searchWeakDefInDylib(const char* name) const; + // copy dylibs to link with in command line order + void dylibs(ld::Internal& state); + + bool inferredArch() const { return _inferredArch; } + + uint32_t nextInputOrdinal() const { return _nextInputOrdinal++; } + + // for -print_statistics + uint64_t _totalObjectSize; + uint64_t _totalArchiveSize; + uint32_t _totalObjectLoaded; + uint32_t _totalArchivesLoaded; + uint32_t _totalDylibsLoaded; + + +private: + void inferArchitecture(Options& opts, const char** archName); + const char* fileArch(const uint8_t* p, unsigned len); + ld::File* makeFile(const Options::FileInfo& info); + ld::File* addDylib(ld::dylib::File* f, const Options::FileInfo& info, uint64_t mappedLen); + ld::File* addObject(ld::relocatable::File* f, const Options::FileInfo& info, uint64_t mappedLen); + ld::File* addArchive(ld::File* f, const Options::FileInfo& info, uint64_t mappedLen); + void logTraceInfo (const char* format, ...) const; + void logDylib(ld::File*, bool indirect); + void logArchive(ld::File*) const; + void createIndirectDylibs(); + void checkDylibClientRestrictions(ld::dylib::File*); + void createOpaqueFileSections(); + + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map<const char*, ld::dylib::File*, __gnu_cxx::hash<const char*>, CStringEquals> InstallNameToDylib; + + const Options& _options; + std::vector<ld::File*> _inputFiles; + mutable std::set<class ld::File*> _archiveFilesLogged; + InstallNameToDylib _installPathToDylibs; + std::set<ld::dylib::File*> _allDylibs; + ld::dylib::File* _bundleLoader; + mutable uint32_t _nextInputOrdinal; + bool _allDirectDylibsLoaded; + bool _inferredArch; +}; + +} // namespace tool +} // namespace ld + +#endif // __INPUT_FILES_H__ diff --git a/src/ld/LTOReader.hpp b/src/ld/LTOReader.hpp deleted file mode 100644 index 2e560cc..0000000 --- a/src/ld/LTOReader.hpp +++ /dev/null @@ -1,742 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __LTO_READER_H__ -#define __LTO_READER_H__ - -#include <stdlib.h> -#include <mach-o/dyld.h> -#include <vector> -#include <ext/hash_set> -#include <ext/hash_map> - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ObjectFile.h" -#include "Options.h" - -#include "llvm-c/lto.h" - - -namespace lto { - - -// -// Reference handles Atom references. These references facilitate -// symbol resolution. -// - -class Reference : public ObjectFile::Reference -{ -public: - Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { } - Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { } - - bool isTargetUnbound() const { return fTargetAtom == NULL; } - bool isFromTargetUnbound() const { return true; } - uint8_t getKind() const { return 0; } - uint64_t getFixUpOffset() const { return 0; } - const char * getTargetName() const { return fTargetName; } - ObjectFile::Atom& getTarget() const { return *fTargetAtom; } - uint64_t getTargetOffset() const { return 0; } - bool hasFromTarget() const { return false; } - ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)NULL); } - const char * getFromTargetName() const { return NULL; } - uint64_t getFromTargetOffset() const { return 0; } - TargetBinding getTargetBinding() const; - TargetBinding getFromTargetBinding() const { return kDontBind; } - void setTarget (ObjectFile::Atom& a, uint64_t offset) - { fTargetAtom = &a; } - void setFromTarget(ObjectFile::Atom &a) { } - const char * getDescription() const; - -private: - const char * fTargetName; - ObjectFile::Atom * fTargetAtom; -}; - - -ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const -{ - if ( fTargetAtom == NULL ) - return kUnboundByName; - else if ( fTargetName == NULL ) - return kBoundDirectly; - else - return kBoundByName; -} - -const char* Reference::getDescription() const -{ - static char temp[256]; - strcpy(temp, "reference to "); - if ( fTargetName != NULL ) - strcat(temp, fTargetName); - else - strcat(temp, fTargetAtom->getDisplayName()); - return temp; -} - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - virtual bool hasFixedAddress() const { return fFixedAddress; } - - static Segment fgBootstrapSegment; - -private: - const char* fName; - const bool fReadable; - const bool fWritable; - const bool fExecutable; - const bool fFixedAddress; -}; - -Segment Segment:: fgBootstrapSegment("__TEMP", true, false, false, false); - - - - -// -// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, -// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After -// optimization is performed, real Atoms are created for these symobls. However these real Atoms -// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate -// methods to real atom. -// -class Atom : public ObjectFile::Atom -{ -public: - Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom); - - ObjectFile::Reader* getFile() const { return (ObjectFile::Reader*)&fOwner; } - bool getTranslationUnitSource (const char **dir, const char **name) const - { return fRealAtom->getTranslationUnitSource(dir, name); } - const char * getName () const { return fName; } - const char * getDisplayName() const { return this->getName(); } - Scope getScope() const { return (fRealAtom ? fRealAtom->getScope() : fScope); } - DefinitionKind getDefinitionKind() const { return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); } - SymbolTableInclusion getSymbolTableInclusion() const - { return (fRealAtom ? fRealAtom->getSymbolTableInclusion() : ObjectFile::Atom::kSymbolTableIn); } - bool dontDeadStrip() const { return false; } - bool isZeroFill() const { return (fRealAtom ? fRealAtom->isZeroFill() : false); } - bool isThumb() const { return false; } - uint64_t getSize() const { return (fRealAtom ? fRealAtom->getSize() : 0); } - std::vector<ObjectFile::Reference*>& getReferences() const - { return (fRealAtom ? fRealAtom->getReferences() : (std::vector<ObjectFile::Reference*>&)fReferences); } - bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); } - const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); } - // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection. - class ObjectFile::Section * getSection() const { return fSection; } - ObjectFile::Segment& getSegment() const { return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); } - uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); } - ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); } - std::vector<ObjectFile::LineInfo>* getLineInfo() const { return (fRealAtom ? fRealAtom->getLineInfo() : NULL); } - ObjectFile::Alignment getAlignment() const { return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); } - void copyRawContent(uint8_t buffer[]) const - { if (fRealAtom) fRealAtom->copyRawContent(buffer); } - void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); else fScope = s; } - - void setRealAtom (ObjectFile::Atom *atom) - { fRealAtom = atom; } - ObjectFile::Atom * getRealAtom() { return fRealAtom; } - void addReference(ObjectFile::Reference *ref) - { fReferences.push_back(ref); } - - void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); } - void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); } - -private: - class Reader& fOwner; - const char* fName; - ObjectFile::Atom::Scope fScope; - ObjectFile::Atom::DefinitionKind fKind; - uint8_t fAlignment; - ObjectFile::Atom* fRealAtom; - std::vector<ObjectFile::Reference*> fReferences; -}; - - -Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom) -: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL) -{ - // every Atom references the InternalAtom for its reader - fReferences.push_back(new Reference(internalAtom)); -} - - -// -// ld64 only tracks non-internal symbols from an llvm bitcode file. -// We model this by having an InternalAtom which represent all internal functions and data. -// All non-interal symbols from a bitcode file are represented by a Atom -// and each Atom has a reference to the InternalAtom. The InternalAtom -// also has references to each symbol external to the bitcode file. -// -class InternalAtom : public ObjectFile::Atom -{ -public: - InternalAtom(class Reader& owner) : fOwner(owner) {} - - ObjectFile::Reader * getFile() const { return (ObjectFile::Reader*)&fOwner; } - bool getTranslationUnitSource (const char **dir, const char **name) const - { return false; } - const char * getName () const { return "__llvm-internal-atom"; } - const char * getDisplayName() const { return "llvm bitcode"; } - Scope getScope() const { return scopeTranslationUnit; } - DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - SymbolTableInclusion getSymbolTableInclusion() const { return kSymbolTableNotIn; } - bool dontDeadStrip() const { return false; } - bool isZeroFill() const { return false; } - bool isThumb() const { return false; } - uint64_t getSize() const { return 0; } - std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)fReferences; } - bool mustRemainInSection() const { return false; } - const char * getSectionName() const { return NULL; } - class ObjectFile::Section * getSection() const { return NULL; } - ObjectFile::Segment& getSegment() const { return Segment::fgBootstrapSegment; } - uint32_t getOrdinal() const { return 0; } - ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; } - ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - void copyRawContent(uint8_t buffer[]) const { } - void setScope(Scope s) { } - - void addReference(const char* targetName); - -private: - class Reader& fOwner; - std::vector<ObjectFile::Reference*> fReferences; -}; - - -void InternalAtom::addReference(const char* name) -{ - fReferences.push_back(new Reference(name)); -} - - - - -class RemovableAtoms -{ -public: - RemovableAtoms(std::set<ObjectFile::Atom*>& iAtoms) : fAtoms(iAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { - return ( fAtoms.count(atom) != 0 ); - } - -private: - std::set<ObjectFile::Atom*>& fAtoms; -}; - - - -// -// LLVM bitcode file reader -// -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); - static const char* fileKind(const uint8_t* fileContent); - static bool loaded() { return (::lto_get_version() != NULL); } - Reader(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, - const ObjectFile::ReaderOptions&, cpu_type_t arch); - virtual ~Reader(); - - virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return (std::vector<class ObjectFile::Atom*>&)(fAtoms); } - virtual std::vector<class ObjectFile::Atom*>* 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<Stab>* getStabs() { return NULL; } - virtual bool optimize(const std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms, - std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>&, - std::vector<ObjectFile::Atom*>& newDeadAtoms, - uint32_t nextInputOrdinal, - ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, - const std::vector<const char*>& llvmOptions, - bool allGlobalsAReDeadStripRoots, - int outputKind, bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs); - -private: - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom; - - ObjectFile::Reader* makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal); - static const char* tripletPrefixForArch(cpu_type_t); - - cpu_type_t fArchitecture; - const char* fPath; - time_t fModTime; - lto_module_t fModule; - std::vector<ObjectFile::Atom*> fAtoms; - InternalAtom fInternalAtom; - const ObjectFile::ReaderOptions& fReaderOptions; - static std::set<Reader*> fgReaders; - static bool fgOptimized; -}; - -bool Reader::fgOptimized = false; -std::set<Reader*> Reader::fgReaders; - - -Reader::~Reader() -{ - if ( fModule != NULL ) - ::lto_module_dispose(fModule); -} - -Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, cpu_type_t arch) - : fArchitecture(arch), fPath(strdup(path)), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options) -{ - fgReaders.insert(this); - - fModule = ::lto_module_create_from_memory(fileContent, fileLength); - if ( fModule == NULL ) - throwf("could not parse object file %s: %s", path, lto_get_error_message()); - - fAtoms.push_back(&fInternalAtom); - - uint32_t count = ::lto_module_get_num_symbols(fModule); - for (uint32_t i=0; i < count; ++i) { - const char* name = ::lto_module_get_symbol_name(fModule, i); - lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i); - - // <rdar://problem/6378110> LTO doesn't like dtrace symbols - // ignore dtrace static probes for now - // later when codegen is done and a mach-o file is produces the probes will be processed - if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) - continue; - - ObjectFile::Atom::DefinitionKind kind; - switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { - case LTO_SYMBOL_DEFINITION_REGULAR: - kind = ObjectFile::Atom::kRegularDefinition; - break; - case LTO_SYMBOL_DEFINITION_TENTATIVE: - kind = ObjectFile::Atom::kTentativeDefinition; - break; - case LTO_SYMBOL_DEFINITION_WEAK: - kind = ObjectFile::Atom::kWeakDefinition; - break; - case LTO_SYMBOL_DEFINITION_UNDEFINED: - case LTO_SYMBOL_DEFINITION_WEAKUNDEF: - kind = ObjectFile::Atom::kExternalDefinition; - break; - default: - throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); - } - - // make LLVM atoms for definitions and a reference for undefines - if ( kind != ObjectFile::Atom::kExternalDefinition ) { - ObjectFile::Atom::Scope scope; - switch ( attr & LTO_SYMBOL_SCOPE_MASK) { - case LTO_SYMBOL_SCOPE_INTERNAL: - scope = ObjectFile::Atom::scopeTranslationUnit; - break; - case LTO_SYMBOL_SCOPE_HIDDEN: - scope = ObjectFile::Atom::scopeLinkageUnit; - break; - case LTO_SYMBOL_SCOPE_DEFAULT: - scope = ObjectFile::Atom::scopeGlobal; - break; - default: - throwf("unknown scope for symbol %s in bitcode file %s", name, path); - } - // only make atoms for non-internal symbols - if ( scope == ObjectFile::Atom::scopeTranslationUnit ) - continue; - uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); - // make Atom - fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom)); - } - else { - // add to list of external references - fInternalAtom.addReference(name); - } - } -} - -const char* Reader::tripletPrefixForArch(cpu_type_t arch) -{ - switch (arch) { - case CPU_TYPE_POWERPC: - return "powerpc-"; - case CPU_TYPE_POWERPC64: - return "powerpc64-"; - case CPU_TYPE_I386: - return "i386-"; - case CPU_TYPE_X86_64: - return "x86_64-"; - case CPU_TYPE_ARM: - return "arm"; - } - return ""; -} - -bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) -{ - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); -} - -const char* Reader::fileKind(const uint8_t* p) -{ - if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { - uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); - switch (arch) { - case CPU_TYPE_POWERPC: - return "ppc"; - case CPU_TYPE_I386: - return "i386"; - case CPU_TYPE_X86_64: - return "x86_64"; - case CPU_TYPE_ARM: - return "arm"; - } - return "unknown bitcode architecture"; - } - return NULL; -} - -bool Reader::optimize(const std::vector<ObjectFile::Atom *>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms, - std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>& deadAtoms, - std::vector<ObjectFile::Atom*>& newlyDeadAtoms, - uint32_t nextInputOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom, - const std::vector<const char*>& llvmOptions, - bool allGlobalsAReDeadStripRoots, - int okind, bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) -{ - // this method is call on all Readers. We want the first call to trigger optimization - // across all Readers and the subsequent calls to do nothing. - if ( fgOptimized ) - return false; - fgOptimized = true; - - Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency - - // print out LTO version string if -v was used - if ( verbose ) - fprintf(stderr, "%s\n", lto_get_version()); - - // create optimizer and add each Reader - lto_code_gen_t generator = ::lto_codegen_create(); - for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { - if ( ::lto_codegen_add_module(generator, (*it)->fModule) ) - throwf("lto: could not merge in %s because %s", (*it)->fPath, ::lto_get_error_message()); - } - - // add any -mllvm command line options - for (std::vector<const char*>::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { - ::lto_codegen_debug_options(generator, *it); - } - - // The atom graph uses directed edges (references). Collect all references where - // originating atom is not part of any LTO Reader. This allows optimizer to optimize an - // external (i.e. not originated from same .o file) reference if all originating atoms are also - // defined in llvm bitcode file. - CStringSet nonLLVMRefs; - CStringToAtom llvmAtoms; - bool hasNonllvmAtoms = false; - for (std::vector<ObjectFile::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - // only look at references come from an atom that is not an llvm atom - if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) { - // remember if we've seen any atoms not from an llvm reader and not from the writer - if ( atom->getFile() != writer ) - hasNonllvmAtoms = true; - std::vector<ObjectFile::Reference*>& refs = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { - ObjectFile::Reference* ref = *ri; - // add target name to set if target is an llvm atom - if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) { - nonLLVMRefs.insert(ref->getTargetName()); - } - } - } - else { - const char* name = atom->getName(); - if ( name != NULL ) - llvmAtoms[name] = (Atom*)atom; - } - } - // if entry point is in a llvm bitcode file, it must be preserved by LTO - if ( entryPointAtom != NULL ) { - if ( fgReaders.count((Reader*)(entryPointAtom->getFile())) != 0 ) - nonLLVMRefs.insert(entryPointAtom->getName()); - } - - // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions - // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced - // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead - // atom so that the linker can replace it with the mach-o one later. - CStringToAtom deadllvmAtoms; - for (std::set<ObjectFile::Atom*>::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - if ( fgReaders.count((Reader*)(atom->getFile())) != 0 ) { - const char* name = atom->getName(); - ::lto_codegen_add_must_preserve_symbol(generator, name); - deadllvmAtoms[name] = (Atom*)atom; - } - } - - - // tell code generator about symbols that must be preserved - for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { - const char* name = it->first; - Atom* atom = it->second; - // Include llvm Symbol in export list if it meets one of following two conditions - // 1 - atom scope is global (and not linkage unit). - // 2 - included in nonLLVMRefs set. - // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - } - - // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) - if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) { - if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { - // HACK, no good way to tell linker we are all done, so just quit - exit(0); - } - warning("could not produce merged bitcode file"); - } - - // set code-gen model - lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - switch ( outputKind ) { - case Options::kDynamicExecutable: - case Options::kPreload: - if ( pie ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kObjectFile: // ?? Is this appropriate ? - case Options::kDyld: - case Options::kKextBundle: - if ( allowTextRelocs ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - break; - case Options::kStaticExecutable: - // darwin x86_64 "static" code model is really dynamic code model - if ( fArchitecture == CPU_TYPE_X86_64 ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_STATIC; - break; - } - if ( ::lto_codegen_set_pic_model(generator, model) ) - throwf("could not create set codegen model: %s", lto_get_error_message()); - - // if requested, save off merged bitcode file - if ( saveTemps ) { - char tempBitcodePath[MAXPATHLEN]; - strcpy(tempBitcodePath, outputFilePath); - strcat(tempBitcodePath, ".lto.bc"); - ::lto_codegen_write_merged_modules(generator, tempBitcodePath); - } - -#if LTO_API_VERSION >= 3 - // find assembler next to linker - char path[PATH_MAX]; - uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { - char* lastSlash = strrchr(path, '/'); - if ( lastSlash != NULL ) { - strcpy(lastSlash+1, "as"); - struct stat statInfo; - if ( stat(path, &statInfo) == 0 ) - ::lto_codegen_set_assembler_path(generator, path); - } - } -#endif - // run code generator - size_t machOFileLen; - const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); - if ( machOFile == NULL ) - throwf("could not do LTO codegen: %s", ::lto_get_error_message()); - - // if requested, save off temp mach-o file - if ( saveTemps ) { - char tempMachoPath[MAXPATHLEN]; - strcpy(tempMachoPath, outputFilePath); - strcat(tempMachoPath, ".lto.o"); - int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if ( fd != -1) { - ::write(fd, machOFile, machOFileLen); - ::close(fd); - } - // save off merged bitcode file - char tempOptBitcodePath[MAXPATHLEN]; - strcpy(tempOptBitcodePath, outputFilePath); - strcat(tempOptBitcodePath, ".lto.opt.bc"); - ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); - } - - // parse generated mach-o file into a MachOReader - ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal); - - // sync generated mach-o atoms with existing atoms ld knows about - std::vector<ObjectFile::Atom*> machoAtoms = machoReader->getAtoms(); - for (std::vector<ObjectFile::Atom *>::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - const char* name = atom->getName(); - if ( name != NULL ) { - CStringToAtom::iterator pos = llvmAtoms.find(name); - if ( pos != llvmAtoms.end() ) { - // turn Atom into a proxy for this mach-o atom - pos->second->setRealAtom(atom); - } - else { - // an atom of this name was not in the allAtoms list the linker gave us - if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { - // this corresponding to an atom that the linker coalesced away. Ignore it - // Make sure there any dependent atoms are also marked dead - std::vector<ObjectFile::Reference*>& refs = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) { - ObjectFile::Reference* ref = *ri; - if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX - ObjectFile::Atom* targ = &ref->getTarget(); - deadllvmAtoms[targ->getName()] = (Atom*)atom; - } - } - } - else - { - // this is something new that lto conjured up, tell ld its new - newAtoms.push_back(atom); - } - } - } - else { - // ld only knew about named atoms, so this one must be new - newAtoms.push_back(atom); - } - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) { - ObjectFile::Reference* ref = *rit; - const char* targetName = ref->getTargetName(); - CStringToAtom::iterator pos; - if (targetName != NULL) { - switch ( ref->getTargetBinding() ) { - case ObjectFile::Reference::kUnboundByName: - // accumulate unbounded references so that ld can bound them. - additionalUndefines.push_back(targetName); - break; - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - // If mach-o atom is referencing another mach-o atom then - // reference is not going through Atom proxy. Fix it here to ensure that all - // llvm symbol references always go through Atom proxy. - pos = llvmAtoms.find(targetName); - if ( pos != llvmAtoms.end() ) - ref->setTarget(*pos->second, ref->getTargetOffset()); - break; - case ObjectFile::Reference::kDontBind: - break; - } - } - } - } - - // Remove InternalAtoms from ld - for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) { - newlyDeadAtoms.push_back(&((*it)->fInternalAtom)); - } - // Remove Atoms from ld if code generator optimized them away - for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { - // check if setRealAtom() called on this Atom - if ( li->second->getRealAtom() == NULL ) - newlyDeadAtoms.push_back(li->second); - } - - return true; -} - - -ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal) -{ - switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Reader<ppc>::validFile(p) ) - return new mach_o::relocatable::Reader<ppc>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Reader<ppc64>::validFile(p) ) - return new mach_o::relocatable::Reader<ppc64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Reader<x86>::validFile(p) ) - return new mach_o::relocatable::Reader<x86>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Reader<x86_64>::validFile(p) ) - return new mach_o::relocatable::Reader<x86_64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Reader<arm>::validFile(p) ) - return new mach_o::relocatable::Reader<arm>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal); - break; - } - throw "LLVM LTO, file is not of required architecture"; -} - -}; // namespace lto - -extern void printLTOVersion(Options& opts); - -void printLTOVersion(Options& opts) { - const char* vers = lto_get_version(); - if ( vers != NULL ) - fprintf(stderr, "%s\n", vers); -} - - -#endif - diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp new file mode 100644 index 0000000..d905a3d --- /dev/null +++ b/src/ld/LinkEdit.hpp @@ -0,0 +1,1306 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LINKEDIT_HPP__ +#define __LINKEDIT_HPP__ + +#include <stdlib.h> +#include <sys/types.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> + +#include <vector> + +#include "Options.h" +#include "ld.hpp" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +namespace ld { +namespace tool { + +class ByteStream { +private: + std::vector<uint8_t> _data; +public: + std::vector<uint8_t>& bytes() { return _data; } + unsigned long size() const { return _data.size(); } + void reserve(unsigned long l) { _data.reserve(l); } + const uint8_t* start() const { return &_data[0]; } + + void append_uleb128(uint64_t value) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + _data.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + void append_sleb128(int64_t value) { + bool isNeg = ( value < 0 ); + uint8_t byte; + bool more; + do { + byte = value & 0x7F; + value = value >> 7; + if ( isNeg ) + more = ( (value != -1) || ((byte & 0x40) == 0) ); + else + more = ( (value != 0) || ((byte & 0x40) != 0) ); + if ( more ) + byte |= 0x80; + _data.push_back(byte); + } + while( more ); + } + + void append_string(const char* str) { + for (const char* s = str; *s != '\0'; ++s) + _data.push_back(*s); + _data.push_back('\0'); + } + + void append_byte(uint8_t byte) { + _data.push_back(byte); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + void pad_to_size(unsigned int alignment) { + while ( (_data.size() % alignment) != 0 ) + _data.push_back(0); + } +}; + + +class LinkEditAtom : public ld::Atom +{ +public: + + // overrides of ld::Atom + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual uint64_t objectAddress() const { return 0; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + + virtual void encode() const = 0; + + LinkEditAtom(const Options& opts, ld::Internal& state, + OutputFile& writer, const ld::Section& sect, + unsigned int pointerSize) + : ld::Atom(sect, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeUnclassified, ld::Atom::symbolTableNotIn, + false, false, false, ld::Atom::Alignment(log2(pointerSize))), + _options(opts), _state(state), _writer(writer), + _encoded(false) { } +protected: + const Options& _options; + ld::Internal& _state; + OutputFile& _writer; + mutable ByteStream _encodedData; + mutable bool _encoded; +}; + +uint64_t LinkEditAtom::size() const +{ + assert(_encoded); + return _encodedData.size(); +} + +void LinkEditAtom::copyRawContent(uint8_t buffer[]) const +{ + assert(_encoded); + memcpy(buffer, _encodedData.start(), _encodedData.size()); +} + + + + +template <typename A> +class RebaseInfoAtom : public LinkEditAtom +{ +public: + RebaseInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { _encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "rebase info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + struct rebase_tmp + { + rebase_tmp(uint8_t op, uint64_t p1, uint64_t p2=0) : opcode(op), operand1(p1), operand2(p2) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + }; + + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section RebaseInfoAtom<A>::_s_section("__LINKEDIT", "__rebase", ld::Section::typeLinkEdit, true); + + +template <typename A> +void RebaseInfoAtom<A>::encode() const +{ + // omit relocs if this was supposed to be PIE but PIE not possible + if ( _options.positionIndependentExecutable() && this->_writer.pieDisabled ) + return; + + // sort rebase info by type, then address + std::vector<OutputFile::RebaseInfo>& info = this->_writer._rebaseInfo; + std::sort(info.begin(), info.end()); + + // convert to temp encoding that can be more easily optimized + std::vector<rebase_tmp> mid; + uint64_t curSegStart = 0; + uint64_t curSegEnd = 0; + uint32_t curSegIndex = 0; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + for (std::vector<OutputFile::RebaseInfo>::iterator it = info.begin(); it != info.end(); ++it) { + if ( type != it->_type ) { + mid.push_back(rebase_tmp(REBASE_OPCODE_SET_TYPE_IMM, it->_type)); + type = it->_type; + } + if ( address != it->_address ) { + if ( (it->_address < curSegStart) || ( it->_address >= curSegEnd) ) { + if ( ! this->_writer.findSegment(this->_state, it->_address, &curSegStart, &curSegEnd, &curSegIndex) ) + throw "binding address outside range of any segment"; + mid.push_back(rebase_tmp(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, curSegIndex, it->_address - curSegStart)); + } + else { + mid.push_back(rebase_tmp(REBASE_OPCODE_ADD_ADDR_ULEB, it->_address-address)); + } + address = it->_address; + } + mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); + address += sizeof(pint_t); + } + mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0)); + + // optimize phase 1, compress packed runs of pointers + rebase_tmp* dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (src->operand1 == 1) ) { + *dst = *src++; + while (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES ) { + dst->operand1 += src->operand1; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 2, combine rebase/add pairs + dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) + && (src->operand1 == 1) + && (src[1].opcode == REBASE_OPCODE_ADD_ADDR_ULEB)) { + dst->opcode = REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 3, compress packed runs of REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB with + // same addr delta into one REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[2].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) + && (src[2].operand1 == delta) ) { + // found at least three in a row, this is worth compressing + dst->opcode = REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = REBASE_OPCODE_DONE; + + // optimize phase 4, use immediate encodings + for (rebase_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == REBASE_OPCODE_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = REBASE_OPCODE_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + else if ( (p->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (p->operand1 < 15) ) { + p->opcode = REBASE_OPCODE_DO_REBASE_IMM_TIMES; + } + } + + // convert to compressed encoding + const static bool log = false; + this->_encodedData.reserve(info.size()*2); + bool done = false; + for (typename std::vector<rebase_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case REBASE_OPCODE_DONE: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DONE()\n"); + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + this->_encodedData.append_byte(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_IMM_TIMES(%lld)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_IMM_TIMES | it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%lld)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); + this->_encodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); + this->_encodedData.append_uleb128(it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + } + } + + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + if (log) fprintf(stderr, "total rebase info size = %ld\n", this->_encodedData.size()); +} + + +template <typename A> +class BindingInfoAtom : public LinkEditAtom +{ +public: + BindingInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "binding info"; } + // overrides of LinkEditAtom + virtual void encode() const; + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct binding_tmp + { + binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL) + : opcode(op), operand1(p1), operand2(p2), name(s) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + const char* name; + }; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section BindingInfoAtom<A>::_s_section("__LINKEDIT", "__binding", ld::Section::typeLinkEdit, true); + + +template <typename A> +void BindingInfoAtom<A>::encode() const +{ + // sort by library, symbol, type, then address + std::vector<OutputFile::BindingInfo>& info = this->_writer._bindingInfo; + std::sort(info.begin(), info.end()); + + // convert to temp encoding that can be more easily optimized + std::vector<binding_tmp> mid; + uint64_t curSegStart = 0; + uint64_t curSegEnd = 0; + uint32_t curSegIndex = 0; + int ordinal = 0x80000000; + const char* symbolName = NULL; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + int64_t addend = 0; + for (std::vector<OutputFile::BindingInfo>::const_iterator it = info.begin(); it != info.end(); ++it) { + if ( ordinal != it->_libraryOrdinal ) { + if ( it->_libraryOrdinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, it->_libraryOrdinal)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, it->_libraryOrdinal)); + } + ordinal = it->_libraryOrdinal; + } + if ( symbolName != it->_symbolName ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->_flags, 0, it->_symbolName)); + symbolName = it->_symbolName; + } + if ( type != it->_type ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->_type)); + type = it->_type; + } + if ( address != it->_address ) { + if ( (it->_address < curSegStart) || ( it->_address >= curSegEnd) ) { + if ( ! this->_writer.findSegment(this->_state, it->_address, &curSegStart, &curSegEnd, &curSegIndex) ) + throw "binding address outside range of any segment"; + mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, curSegIndex, it->_address - curSegStart)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->_address-address)); + } + address = it->_address; + } + if ( addend != it->_addend ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->_addend)); + addend = it->_addend; + } + mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); + address += sizeof(pint_t); + } + mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); + + + // optimize phase 1, combine bind/add pairs + binding_tmp* dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ( (src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) ) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) { + p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // convert to compressed encoding + const static bool log = false; + this->_encodedData.reserve(info.size()*2); + bool done = false; + for (typename std::vector<binding_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case BIND_OPCODE_DONE: + if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); + this->_encodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); + this->_encodedData.append_string(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); + this->_encodedData.append_sleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + this->_encodedData.append_uleb128(it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + } + } + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + if (log) fprintf(stderr, "total binding info size = %ld\n", this->_encodedData.size()); +} + + + +template <typename A> +class WeakBindingInfoAtom : public LinkEditAtom +{ +public: + WeakBindingInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { _encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "weak binding info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct WeakBindingSorter + { + bool operator()(const OutputFile::BindingInfo& left, const OutputFile::BindingInfo& right) + { + // sort by symbol, type, address + if ( left._symbolName != right._symbolName ) + return ( strcmp(left._symbolName, right._symbolName) < 0 ); + if ( left._type != right._type ) + return (left._type < right._type); + return (left._address < right._address); + } + }; + + struct binding_tmp + { + binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL) + : opcode(op), operand1(p1), operand2(p2), name(s) {} + uint8_t opcode; + uint64_t operand1; + uint64_t operand2; + const char* name; + }; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section WeakBindingInfoAtom<A>::_s_section("__LINKEDIT", "__weak_binding", ld::Section::typeLinkEdit, true); + + +template <typename A> +void WeakBindingInfoAtom<A>::encode() const +{ + // sort by symbol, type, address + std::vector<OutputFile::BindingInfo>& info = this->_writer._weakBindingInfo; + if ( info.size() == 0 ) { + // short circuit if no weak binding needed + this->_encoded = true; + return; + } + std::sort(info.begin(), info.end(), WeakBindingSorter()); + + // convert to temp encoding that can be more easily optimized + std::vector<binding_tmp> mid; + mid.reserve(info.size()); + uint64_t curSegStart = 0; + uint64_t curSegEnd = 0; + uint32_t curSegIndex = 0; + const char* symbolName = NULL; + uint8_t type = 0; + uint64_t address = (uint64_t)(-1); + int64_t addend = 0; + for (typename std::vector<OutputFile::BindingInfo>::const_iterator it = info.begin(); it != info.end(); ++it) { + if ( symbolName != it->_symbolName ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->_flags, 0, it->_symbolName)); + symbolName = it->_symbolName; + } + // non-weak symbols just have BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + // weak symbols have SET_SEG, ADD_ADDR, SET_ADDED, DO_BIND + if ( it->_type != BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB ) { + if ( type != it->_type ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->_type)); + type = it->_type; + } + if ( address != it->_address ) { + if ( (it->_address < curSegStart) || ( it->_address >= curSegEnd) ) { + if ( ! this->_writer.findSegment(this->_state, it->_address, &curSegStart, &curSegEnd, &curSegIndex) ) + throw "binding address outside range of any segment"; + mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, curSegIndex, it->_address - curSegStart)); + } + else { + mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->_address-address)); + } + address = it->_address; + } + if ( addend != it->_addend ) { + mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->_addend)); + addend = it->_addend; + } + mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); + address += sizeof(pint_t); + } + } + mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); + + + // optimize phase 1, combine bind/add pairs + binding_tmp* dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + if ( (src->opcode == BIND_OPCODE_DO_BIND) + && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { + dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; + dst->operand1 = src[1].operand1; + ++src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with + // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + dst = &mid[0]; + for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { + uint64_t delta = src->operand1; + if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src[1].operand1 == delta) ) { + // found at least two in a row, this is worth compressing + dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; + dst->operand1 = 1; + dst->operand2 = delta; + ++src; + while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (src->operand1 == delta) ) { + dst->operand1++; + ++src; + } + --src; + ++dst; + } + else { + *dst++ = *src; + } + } + dst->opcode = BIND_OPCODE_DONE; + + // optimize phase 3, use immediate encodings + for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { + if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) + && (p->operand1 < (15*sizeof(pint_t))) + && ((p->operand1 % sizeof(pint_t)) == 0) ) { + p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; + p->operand1 = p->operand1/sizeof(pint_t); + } + } + dst->opcode = BIND_OPCODE_DONE; + + + // convert to compressed encoding + const static bool log = false; + this->_encodedData.reserve(info.size()*2); + bool done = false; + for (typename std::vector<binding_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { + switch ( it->opcode ) { + case BIND_OPCODE_DONE: + if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); + this->_encodedData.append_byte(BIND_OPCODE_DONE); + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); + this->_encodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); + this->_encodedData.append_string(it->name); + break; + case BIND_OPCODE_SET_TYPE_IMM: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); + this->_encodedData.append_sleb128(it->operand1); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); + this->_encodedData.append_uleb128(it->operand1); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); + this->_encodedData.append_uleb128(it->operand1); + this->_encodedData.append_uleb128(it->operand2); + break; + } + } + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + if (log) fprintf(stderr, "total weak binding info size = %ld\n", this->_encodedData.size()); + +} + + + +template <typename A> +class LazyBindingInfoAtom : public LinkEditAtom +{ +public: + LazyBindingInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) {_encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "lazy binding info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section LazyBindingInfoAtom<A>::_s_section("__LINKEDIT", "__lazy_binding", ld::Section::typeLinkEdit, true); + + + +template <typename A> +void LazyBindingInfoAtom<A>::encode() const +{ + // stream all lazy bindings and record start offsets + std::vector<OutputFile::BindingInfo>& info = this->_writer._lazyBindingInfo; + for (std::vector<OutputFile::BindingInfo>::const_iterator it = info.begin(); it != info.end(); ++it) { + // record start offset for use by stub helper + this->_writer.setLazyBindingInfoOffset(it->_address, this->_encodedData.size()); + + // write address to bind + uint64_t segStart = 0; + uint64_t segEnd = 0; + uint32_t segIndex = 0; + if ( ! this->_writer.findSegment(this->_state, it->_address, &segStart, &segEnd, &segIndex) ) + throw "lazy binding address outside range of any segment"; + this->_encodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex); + this->_encodedData.append_uleb128(it->_address - segStart); + + // write ordinal + if ( it->_libraryOrdinal <= 0 ) { + // special lookups are encoded as negative numbers in BindingInfo + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->_libraryOrdinal & BIND_IMMEDIATE_MASK) ); + } + else if ( it->_libraryOrdinal <= 15 ) { + // small ordinals are encoded in opcode + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->_libraryOrdinal); + } + else { + this->_encodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); + this->_encodedData.append_uleb128(it->_libraryOrdinal); + } + // write symbol name + this->_encodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->_flags); + this->_encodedData.append_string(it->_symbolName); + // write do bind + this->_encodedData.append_byte(BIND_OPCODE_DO_BIND); + this->_encodedData.append_byte(BIND_OPCODE_DONE); + } + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + //fprintf(stderr, "lazy binding info size = %ld, for %ld entries\n", _encodedData.size(), allLazys.size()); +} + + + +template <typename A> +class ExportInfoAtom : public LinkEditAtom +{ +public: + ExportInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { _encoded = true; } + + // overrides of ld::Atom + virtual const char* name() const { return "export info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + const ld::Atom* stubForResolverFunction(const ld::Atom* resolver) const; + + struct TrieEntriesSorter + { + TrieEntriesSorter(const Options& o) : _options(o) {} + + bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) + { + unsigned int leftOrder; + unsigned int rightOrder; + _options.exportedSymbolOrder(left.name, &leftOrder); + _options.exportedSymbolOrder(right.name, &rightOrder); + if ( leftOrder != rightOrder ) + return (leftOrder < rightOrder); + else + return (left.address < right.address); + } + private: + const Options& _options; + }; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section ExportInfoAtom<A>::_s_section("__LINKEDIT", "__export", ld::Section::typeLinkEdit, true); + +template <typename A> +const ld::Atom* ExportInfoAtom<A>::stubForResolverFunction(const ld::Atom* resolver) const +{ + for (std::vector<ld::Internal::FinalSection*>::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( (sect->type() == ld::Section::typeStub) || (sect->type() == ld::Section::typeStubClose) ) { + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( strcmp(atom->name(), resolver->name()) == 0 ) + return atom; + } + } + } + assert(0 && "no stub for resolver function"); + return NULL; +} + + +template <typename A> +void ExportInfoAtom<A>::encode() const +{ + // make vector of mach_o::trie::Entry for all exported symbols + std::vector<const ld::Atom*>& exports = this->_writer._exportedAtoms; + uint64_t imageBaseAddress = this->_writer.headerAndLoadCommandsSection->address; + std::vector<mach_o::trie::Entry> entries; + entries.reserve(exports.size()); + for (std::vector<const ld::Atom*>::const_iterator it = exports.begin(); it != exports.end(); ++it) { + const ld::Atom* atom = *it; + mach_o::trie::Entry entry; + uint64_t flags = (atom->contentType() == ld::Atom::typeTLV) ? EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL : EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + uint64_t other = 0; + uint64_t address = atom->finalAddress() - imageBaseAddress; + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) + flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + if ( atom->definition() == ld::Atom::definitionProxy ) { + entry.name = atom->name(); + entry.flags = flags | EXPORT_SYMBOL_FLAGS_REEXPORT; + entry.other = this->_writer.compressedOrdinalForAtom(atom); + if ( entry.other == BIND_SPECIAL_DYLIB_SELF ) { + warning("not adding explict export for symbol %s because it is already re-exported from dylib %s", entry.name, atom->file()->path()); + continue; + } + if ( atom->isAlias() ) { + // alias proxy means symbol was re-exported with a name change + const ld::Atom* aliasOf = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + aliasOf = fit->u.target; + } + } + assert(aliasOf != NULL); + entry.importName = aliasOf->name(); + } + else { + // symbol name stays same as re-export + entry.importName = atom->name(); + } + entries.push_back(entry); + //fprintf(stderr, "re-export %s from lib %llu as %s\n", entry.importName, entry.other, entry.name); + } + else { + if ( atom->isThumb() ) + address |= 1; + if ( atom->contentType() == ld::Atom::typeResolver ) { + flags |= EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER; + // set normal lookup to return stub address + // and add resolver function in new location that newer dyld's can access + other = address; + const ld::Atom* stub = stubForResolverFunction(atom); + address = stub->finalAddress() - imageBaseAddress; + if ( stub->isThumb() ) + address |= 1; + } + entry.name = atom->name(); + entry.flags = flags; + entry.address = address; + entry.other = other; + entry.importName = NULL; + entries.push_back(entry); + } + } + + // sort vector by -exported_symbols_order, and any others by address + std::sort(entries.begin(), entries.end(), TrieEntriesSorter(_options)); + + // create trie + mach_o::trie::makeTrie(entries, this->_encodedData.bytes()); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; +} + + +template <typename A> +class SplitSegInfoAtom : public LinkEditAtom +{ +public: + SplitSegInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "split seg info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + void addSplitSegInfo(uint64_t address, ld::Fixup::Kind k) const; + void uleb128EncodeAddresses(const std::vector<uint64_t>& locations) const; + + mutable std::vector<uint64_t> _32bitPointerLocations; + mutable std::vector<uint64_t> _64bitPointerLocations; + mutable std::vector<uint64_t> _ppcHi16Locations; + + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section SplitSegInfoAtom<A>::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); + +template <> +void SplitSegInfoAtom<x86_64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + _32bitPointerLocations.push_back(address); + break; + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + _64bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + +template <> +void SplitSegInfoAtom<x86>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + _32bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + +template <> +void SplitSegInfoAtom<arm>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStoreLittleEndian32: + _32bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + + +template <> +void SplitSegInfoAtom<ppc>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStorePPCPicHigh16AddLow: + _ppcHi16Locations.push_back(address); + break; + case ld::Fixup::kindStoreBigEndian32: + _32bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + + +template <> +void SplitSegInfoAtom<ppc64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const +{ + switch (kind) { + case ld::Fixup::kindStorePPCPicHigh16AddLow: + _ppcHi16Locations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} + + +template <typename A> +void SplitSegInfoAtom<A>::uleb128EncodeAddresses(const std::vector<uint64_t>& locations) const +{ + pint_t addr = this->_options.baseAddress(); + for(typename std::vector<uint64_t>::const_iterator it = locations.begin(); it != locations.end(); ++it) { + pint_t nextAddr = *it; + //fprintf(stderr, "nextAddr=0x%0llX\n", (uint64_t)nextAddr); + uint64_t delta = nextAddr - addr; + //fprintf(stderr, "delta=0x%0llX\n", delta); + if ( delta == 0 ) + throw "double split seg info for same address"; + // uleb128 encode + uint8_t byte; + do { + byte = delta & 0x7F; + delta &= ~0x7F; + if ( delta != 0 ) + byte |= 0x80; + this->_encodedData.append_byte(byte); + delta = delta >> 7; + } + while( byte >= 0x80 ); + addr = nextAddr; + } +} + + +template <typename A> +void SplitSegInfoAtom<A>::encode() const +{ + // sort into group by pointer adjustment kind + std::vector<OutputFile::SplitSegInfoEntry>& info = this->_writer._splitSegInfos; + for (std::vector<OutputFile::SplitSegInfoEntry>::const_iterator it = info.begin(); it != info.end(); ++it) { + this->addSplitSegInfo(it->address, it->kind); + } + + // delta compress runs of addresses + this->_encodedData.reserve(8192); + if ( _32bitPointerLocations.size() != 0 ) { + this->_encodedData.append_byte(1); + //fprintf(stderr, "type 1:\n"); + std::sort(_32bitPointerLocations.begin(), _32bitPointerLocations.end()); + this->uleb128EncodeAddresses(_32bitPointerLocations); + this->_encodedData.append_byte(0); // terminator + } + + if ( _64bitPointerLocations.size() != 0 ) { + this->_encodedData.append_byte(2); + //fprintf(stderr, "type 2:\n"); + std::sort(_64bitPointerLocations.begin(), _64bitPointerLocations.end()); + this->uleb128EncodeAddresses(_64bitPointerLocations); + this->_encodedData.append_byte(0); // terminator + } + + if ( _ppcHi16Locations.size() != 0 ) { + this->_encodedData.append_byte(3); + //fprintf(stderr, "type 3:\n"); + std::sort(_ppcHi16Locations.begin(), _ppcHi16Locations.end()); + this->uleb128EncodeAddresses(_ppcHi16Locations); + this->_encodedData.append_byte(0); // terminator + } + + // always add zero byte to mark end + this->_encodedData.append_byte(0); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; + + // clean up temporaries + _32bitPointerLocations.clear(); + _64bitPointerLocations.clear(); + _ppcHi16Locations.clear(); +} + + +template <typename A> +class FunctionStartsAtom : public LinkEditAtom +{ +public: + FunctionStartsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "function starts"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section FunctionStartsAtom<A>::_s_section("__LINKEDIT", "__funcStarts", ld::Section::typeLinkEdit, true); + + +template <typename A> +void FunctionStartsAtom<A>::encode() const +{ + this->_encodedData.reserve(8192); + const uint64_t badAddress = 1; + uint64_t addr = badAddress; + // delta compress all function addresses + for (std::vector<ld::Internal::FinalSection*>::iterator it = this->_state.sections.begin(); it != this->_state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( sect->type() == ld::Section::typeMachHeader ) { + // start with delta from start of __TEXT + addr = sect->address; + } + else if ( sect->type() == ld::Section::typeCode ) { + assert(addr != badAddress); + std::vector<const ld::Atom*>& atoms = sect->atoms; + for (std::vector<const ld::Atom*>::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + uint64_t nextAddr = atom->finalAddress(); + if ( atom->isThumb() ) + nextAddr |= 1; + uint64_t delta = nextAddr - addr; + if ( delta != 0 ) + this->_encodedData.append_uleb128(delta); + addr = nextAddr; + } + } + } + + // terminator + this->_encodedData.append_byte(0); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; +} + + + +} // namespace tool +} // namespace ld + +#endif // __LINKEDIT_HPP__ diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp new file mode 100644 index 0000000..d5438ac --- /dev/null +++ b/src/ld/LinkEditClassic.hpp @@ -0,0 +1,2542 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LINKEDIT_CLASSIC_HPP__ +#define __LINKEDIT_CLASSIC_HPP__ + +#include <stdlib.h> +#include <sys/types.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> + +#include <vector> + +#include "Options.h" +#include "ld.hpp" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +namespace ld { +namespace tool { + + + +class ClassicLinkEditAtom : public ld::Atom +{ +public: + + // overrides of ld::Atom + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual uint64_t objectAddress() const { return 0; } + + virtual void encode() = 0; + virtual bool hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe) { return false; } + + ClassicLinkEditAtom(const Options& opts, ld::Internal& state, + OutputFile& writer, const ld::Section& sect, + unsigned int pointerSize) + : ld::Atom(sect, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeUnclassified, ld::Atom::symbolTableNotIn, + false, false, false, ld::Atom::Alignment(log2(pointerSize))), + _options(opts), _state(state), _writer(writer) { } +protected: + const Options& _options; + ld::Internal& _state; + OutputFile& _writer; +}; + + + +class StringPoolAtom : public ClassicLinkEditAtom +{ +public: + StringPoolAtom(const Options& opts, ld::Internal& state, + OutputFile& writer, int pointerSize); + + // overrides of ld::Atom + virtual const char* name() const { return "string pool"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode() { } + + int32_t add(const char* name); + int32_t addUnique(const char* name); + int32_t emptyString() { return 1; } + const char* stringForIndex(int32_t) const; + uint32_t currentOffset(); + +private: + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + enum { kBufferSize = 0x01000000 }; + typedef __gnu_cxx::hash_map<const char*, int32_t, __gnu_cxx::hash<const char*>, CStringEquals> StringToOffset; + + const uint32_t _pointerSize; + std::vector<char*> _fullBuffers; + char* _currentBuffer; + uint32_t _currentBufferUsed; + StringToOffset _uniqueStrings; + + static ld::Section _s_section; +}; + +ld::Section StringPoolAtom::_s_section("__LINKEDIT", "__string_pool", ld::Section::typeLinkEdit, true); + + +StringPoolAtom::StringPoolAtom(const Options& opts, ld::Internal& state, OutputFile& writer, int pointerSize) + : ClassicLinkEditAtom(opts, state, writer, _s_section, pointerSize), + _pointerSize(pointerSize), _currentBuffer(NULL), _currentBufferUsed(0) +{ + _currentBuffer = new char[kBufferSize]; + // burn first byte of string pool (so zero is never a valid string offset) + _currentBuffer[_currentBufferUsed++] = ' '; + // make offset 1 always point to an empty string + _currentBuffer[_currentBufferUsed++] = '\0'; +} + +uint64_t StringPoolAtom::size() const +{ + // pointer size align size + return (kBufferSize * _fullBuffers.size() + _currentBufferUsed + _pointerSize-1) & (-_pointerSize); +} + +void StringPoolAtom::copyRawContent(uint8_t buffer[]) const +{ + uint64_t offset = 0; + for (unsigned int i=0; i < _fullBuffers.size(); ++i) { + memcpy(&buffer[offset], _fullBuffers[i], kBufferSize); + offset += kBufferSize; + } + memcpy(&buffer[offset], _currentBuffer, _currentBufferUsed); + // zero fill end to align + offset += _currentBufferUsed; + while ( (offset % _pointerSize) != 0 ) + buffer[offset++] = 0; +} + +int32_t StringPoolAtom::add(const char* str) +{ + int32_t offset = kBufferSize * _fullBuffers.size() + _currentBufferUsed; + int lenNeeded = strlcpy(&_currentBuffer[_currentBufferUsed], str, kBufferSize-_currentBufferUsed)+1; + if ( (_currentBufferUsed+lenNeeded) < kBufferSize ) { + _currentBufferUsed += lenNeeded; + } + else { + int copied = kBufferSize-_currentBufferUsed-1; + // change trailing '\0' that strlcpy added to real char + _currentBuffer[kBufferSize-1] = str[copied]; + // alloc next buffer + _fullBuffers.push_back(_currentBuffer); + _currentBuffer = new char[kBufferSize]; + _currentBufferUsed = 0; + // append rest of string + this->add(&str[copied+1]); + } + return offset; +} + +uint32_t StringPoolAtom::currentOffset() +{ + return kBufferSize * _fullBuffers.size() + _currentBufferUsed; +} + + +int32_t StringPoolAtom::addUnique(const char* str) +{ + StringToOffset::iterator pos = _uniqueStrings.find(str); + if ( pos != _uniqueStrings.end() ) { + return pos->second; + } + else { + int32_t offset = this->add(str); + _uniqueStrings[str] = offset; + return offset; + } +} + + +const char* StringPoolAtom::stringForIndex(int32_t index) const +{ + int32_t currentBufferStartIndex = kBufferSize * _fullBuffers.size(); + int32_t maxIndex = currentBufferStartIndex + _currentBufferUsed; + // check for out of bounds + if ( index > maxIndex ) + return ""; + // check for index in _currentBuffer + if ( index > currentBufferStartIndex ) + return &_currentBuffer[index-currentBufferStartIndex]; + // otherwise index is in a full buffer + uint32_t fullBufferIndex = index/kBufferSize; + return &_fullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)]; +} + + + +template <typename A> +class SymbolTableAtom : public ClassicLinkEditAtom +{ +public: + SymbolTableAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : ClassicLinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)), + _stabsStringsOffsetStart(0), _stabsStringsOffsetEnd(0), + _stabsIndexStart(0), _stabsIndexEnd(0) { } + + // overrides of ld::Atom + virtual const char* name() const { return "symbol table"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode(); + virtual bool hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + bool addLocal(const ld::Atom* atom, StringPoolAtom* pool); + void addGlobal(const ld::Atom* atom, StringPoolAtom* pool); + void addImport(const ld::Atom* atom, StringPoolAtom* pool); + uint8_t classicOrdinalForProxy(const ld::Atom* atom); + uint32_t stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool); + uint64_t valueForStab(const ld::relocatable::File::Stab& stab); + uint8_t sectionIndexForStab(const ld::relocatable::File::Stab& stab); + + + mutable std::vector<macho_nlist<P> > _globals; + mutable std::vector<macho_nlist<P> > _locals; + mutable std::vector<macho_nlist<P> > _imports; + + uint32_t _stabsStringsOffsetStart; + uint32_t _stabsStringsOffsetEnd; + uint32_t _stabsIndexStart; + uint32_t _stabsIndexEnd; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section SymbolTableAtom<A>::_s_section("__LINKEDIT", "__symbol_table", ld::Section::typeLinkEdit, true); + + + +template <typename A> +bool SymbolTableAtom<A>::addLocal(const ld::Atom* atom, StringPoolAtom* pool) +{ + macho_nlist<P> entry; + static int s_anonNameIndex = 1; + assert(atom->symbolTableInclusion() != ld::Atom::symbolTableNotIn); + + // set n_strx + const char* symbolName = atom->name(); + char anonName[32]; + if ( this->_options.outputKind() == Options::kObjectFile ) { + if ( atom->contentType() == ld::Atom::typeCString ) { + if ( atom->combine() == ld::Atom::combineByNameAndContent ) { + // don't use 'l' labels for x86_64 strings + // <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped + sprintf(anonName, "LC%u", s_anonNameIndex++); + symbolName = anonName; + } + } + else if ( atom->contentType() == ld::Atom::typeCFI ) { + if ( _options.removeEHLabels() ) + return false; + // synthesize .eh name + if ( strcmp(atom->name(), "CIE") == 0 ) + symbolName = "EH_Frame1"; + else + symbolName = "func.eh"; + } + else if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInWithRandomAutoStripLabel ) { + // make auto-strip anonymous name for symbol + sprintf(anonName, "l%03u", s_anonNameIndex++); + symbolName = anonName; + } + } + entry.set_n_strx(pool->add(symbolName)); + + // set n_type + uint8_t type = N_SECT; + if ( atom->definition() == ld::Atom::definitionAbsolute ) { + type = N_ABS; + } + else if ( (atom->section().type() == ld::Section::typeObjC1Classes) + && (this->_options.outputKind() == Options::kObjectFile) ) { + // __OBJC __class has floating abs symbols for each class data structure + type = N_ABS; + } + if ( atom->scope() == ld::Atom::scopeLinkageUnit ) + type |= N_PEXT; + entry.set_n_type(type); + + // set n_sect (section number of implementation ) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_sect(0); + else + entry.set_n_sect(atom->machoSection()); + + // set n_desc + uint16_t desc = 0; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) + desc |= N_WEAK_DEF; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + entry.set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_value(atom->objectAddress()); + else + entry.set_n_value(atom->finalAddress()); + + // add to array + _locals.push_back(entry); + return true; +} + + +template <typename A> +void SymbolTableAtom<A>::addGlobal(const ld::Atom* atom, StringPoolAtom* pool) +{ + macho_nlist<P> entry; + + // set n_strx + entry.set_n_strx(pool->add(atom->name())); + + // set n_type + if ( atom->definition() == ld::Atom::definitionAbsolute ) { + entry.set_n_type(N_EXT | N_ABS); + } + else if ( (atom->section().type() == ld::Section::typeObjC1Classes) + && (this->_options.outputKind() == Options::kObjectFile) ) { + // __OBJC __class has floating abs symbols for each class data structure + entry.set_n_type(N_EXT | N_ABS); + } + else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) { + entry.set_n_type(N_EXT | N_INDR); + } + else { + entry.set_n_type(N_EXT | N_SECT); + if ( (atom->scope() == ld::Atom::scopeLinkageUnit) && (this->_options.outputKind() == Options::kObjectFile) ) { + if ( this->_options.keepPrivateExterns() ) + entry.set_n_type(N_EXT | N_SECT | N_PEXT); + } + else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) + && (atom->section().type() == ld::Section::typeMachHeader) ) { + // the __mh_execute_header is historical magic and must be an absolute symbol + entry.set_n_type(N_EXT | N_ABS); + } + } + + // set n_sect (section number of implementation) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_sect(0); + else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) + entry.set_n_sect(0); + else + entry.set_n_sect(atom->machoSection()); + + // set n_desc + uint16_t desc = 0; + if ( atom->isThumb() ) + desc |= N_ARM_THUMB_DEF; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip ) + desc |= REFERENCED_DYNAMICALLY; + if ( (atom->contentType() == ld::Atom::typeResolver) && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_SYMBOL_RESOLVER; + if ( atom->dontDeadStrip() && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_NO_DEAD_STRIP; + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) { + desc |= N_WEAK_DEF; + // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden + if ( (atom->scope() == ld::Atom::scopeGlobal) && atom->autoHide() && (this->_options.outputKind() == Options::kObjectFile) ) + desc |= N_WEAK_REF; + } + entry.set_n_desc(desc); + + // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) + if ( atom->definition() == ld::Atom::definitionAbsolute ) + entry.set_n_value(atom->objectAddress()); + else if ( (atom->definition() == ld::Atom::definitionProxy) && (atom->scope() == ld::Atom::scopeGlobal) ) { + if ( atom->isAlias() ) { + // this re-export also renames + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + entry.set_n_value(pool->add(fit->u.target->name())); + } + } + } + else + entry.set_n_value(entry.n_strx()); + } + else + entry.set_n_value(atom->finalAddress()); + + // add to array + _globals.push_back(entry); +} + +template <typename A> +uint8_t SymbolTableAtom<A>::classicOrdinalForProxy(const ld::Atom* atom) +{ + assert(atom->definition() == ld::Atom::definitionProxy); + // when linking for flat-namespace ordinals are always zero + if ( _options.nameSpace() != Options::kTwoLevelNameSpace ) + return 0; + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(atom->file()); + // when linking -undefined dynamic_lookup, unbound symbols use DYNAMIC_LOOKUP_ORDINAL + if ( dylib == NULL ) { + if (_options.undefinedTreatment() == Options::kUndefinedDynamicLookup ) + return DYNAMIC_LOOKUP_ORDINAL; + if (_options.allowedUndefined(atom->name()) ) + return DYNAMIC_LOOKUP_ORDINAL; + } + assert(dylib != NULL); + int ord = this->_writer.dylibToOrdinal(dylib); + if ( ord == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) + return EXECUTABLE_ORDINAL; + return ord; +} + + +template <typename A> +void SymbolTableAtom<A>::addImport(const ld::Atom* atom, StringPoolAtom* pool) +{ + macho_nlist<P> entry; + + // set n_strx + entry.set_n_strx(pool->add(atom->name())); + + // set n_type + if ( this->_options.outputKind() == Options::kObjectFile ) { + if ( (atom->scope() == ld::Atom::scopeLinkageUnit) + && (atom->definition() == ld::Atom::definitionTentative) ) + entry.set_n_type(N_UNDF | N_EXT | N_PEXT); + else + entry.set_n_type(N_UNDF | N_EXT); + } + else { + if ( this->_options.prebind() ) + entry.set_n_type(N_PBUD | N_EXT); + else + entry.set_n_type(N_UNDF | N_EXT); + } + + // set n_sect + entry.set_n_sect(0); + + uint16_t desc = 0; + if ( this->_options.outputKind() != Options::kObjectFile ) { + uint8_t ordinal = this->classicOrdinalForProxy(atom); + //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName()); + SET_LIBRARY_ORDINAL(desc, ordinal); + +#if 0 + // set n_desc ( high byte is library ordinal, low byte is reference type ) + std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(atom); + if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) ) + desc |= REFERENCE_FLAG_UNDEFINED_LAZY; + else + desc |= REFERENCE_FLAG_UNDEFINED_NON_LAZY; +#endif + } + else if ( atom->definition() == ld::Atom::definitionTentative ) { + uint8_t align = atom->alignment().powerOf2; + // always record custom alignment of common symbols to match what compiler does + SET_COMM_ALIGN(desc, align); + } + if ( (this->_options.outputKind() != Options::kObjectFile) + && (atom->definition() == ld::Atom::definitionProxy) + && (atom->combine() == ld::Atom::combineByName) ) { + desc |= N_REF_TO_WEAK; + } + if ( atom->weakImported() ) + desc |= N_WEAK_REF; + entry.set_n_desc(desc); + + // set n_value, zero for import proxy and size for tentative definition + if ( atom->definition() == ld::Atom::definitionTentative ) + entry.set_n_value(atom->size()); + else + entry.set_n_value(0); + + // add to array + _imports.push_back(entry); +} + +template <typename A> +uint8_t SymbolTableAtom<A>::sectionIndexForStab(const ld::relocatable::File::Stab& stab) +{ + // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN + if ( stab.type == N_FUN ) + return stab.other; + else if ( stab.type == N_GSYM ) + return 0; + else if ( stab.atom != NULL ) + return stab.atom->machoSection(); + else + return stab.other; +} + + +template <typename A> +uint64_t SymbolTableAtom<A>::valueForStab(const ld::relocatable::File::Stab& stab) +{ + switch ( stab.type ) { + case N_FUN: + if ( stab.atom == NULL ) { + // <rdar://problem/5591394> Add support to ld64 for N_FUN stabs when used for symbolic constants + return stab.value; + } + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of function N_FUN has size + return stab.atom->size(); + } + else { + // start of function N_FUN has address + return stab.atom->finalAddress(); + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + if ( stab.atom == NULL ) + // some weird assembly files have slines not associated with a function + return stab.value; + else + // all these stab types need their value changed from an offset in the atom to an address + return stab.atom->finalAddress() + stab.value; + case N_STSYM: + case N_LCSYM: + case N_BNSYM: + // all these need address of atom + if ( stab.atom != NULL ) + return stab.atom->finalAddress(); + else + return 0; // <rdar://problem/7811357> work around for mismatch N_BNSYM + case N_ENSYM: + return stab.atom->size(); + case N_SO: + if ( stab.atom == NULL ) { + return 0; + } + else { + if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { + // end of translation unit N_SO has address of end of last atom + return stab.atom->finalAddress() + stab.atom->size(); + } + else { + // start of translation unit N_SO has address of end of first atom + return stab.atom->finalAddress(); + } + } + break; + default: + return stab.value; + } +} + +template <typename A> +uint32_t SymbolTableAtom<A>::stringOffsetForStab(const ld::relocatable::File::Stab& stab, StringPoolAtom* pool) +{ + switch (stab.type) { + case N_SO: + if ( (stab.string == NULL) || stab.string[0] == '\0' ) { + return pool->emptyString(); + break; + } + // fall into uniquing case + case N_SOL: + case N_BINCL: + case N_EXCL: + return pool->addUnique(stab.string); + break; + default: + if ( stab.string == NULL ) + return 0; + else if ( stab.string[0] == '\0' ) + return pool->emptyString(); + else + return pool->add(stab.string); + } + return 0; +} + + + +template <typename A> +bool SymbolTableAtom<A>::hasStabs(uint32_t& ssos, uint32_t& ssoe, uint32_t& sos, uint32_t& soe) +{ + ssos = _stabsStringsOffsetStart; + ssoe = _stabsStringsOffsetEnd; + sos = _stabsIndexStart * sizeof(macho_nlist<P>); + soe = _stabsIndexEnd * sizeof(macho_nlist<P>); + return ( (_stabsIndexStart != _stabsIndexEnd) || (_stabsStringsOffsetStart != _stabsStringsOffsetEnd) ); +} + +template <typename A> +void SymbolTableAtom<A>::encode() +{ + uint32_t symbolIndex = 0; + + // make nlist entries for all local symbols + std::vector<const ld::Atom*>& localAtoms = this->_writer._localAtoms; + _locals.reserve(localAtoms.size()+this->_state.stabs.size()); + this->_writer._localSymbolsStartIndex = 0; + // make nlist entries for all debug notes + _stabsIndexStart = symbolIndex; + _stabsStringsOffsetStart = this->_writer._stringPoolAtom->currentOffset(); + for (std::vector<ld::relocatable::File::Stab>::const_iterator sit=this->_state.stabs.begin(); sit != this->_state.stabs.end(); ++sit) { + macho_nlist<P> entry; + entry.set_n_type(sit->type); + entry.set_n_sect(sectionIndexForStab(*sit)); + entry.set_n_desc(sit->desc); + entry.set_n_value(valueForStab(*sit)); + entry.set_n_strx(stringOffsetForStab(*sit, this->_writer._stringPoolAtom)); + _locals.push_back(entry); + ++symbolIndex; + } + _stabsIndexEnd = symbolIndex; + _stabsStringsOffsetEnd = this->_writer._stringPoolAtom->currentOffset(); + for (std::vector<const ld::Atom*>::const_iterator it=localAtoms.begin(); it != localAtoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( this->addLocal(atom, this->_writer._stringPoolAtom) ) + this->_writer._atomToSymbolIndex[atom] = symbolIndex++; + } + this->_writer._localSymbolsCount = symbolIndex; + + + // make nlist entries for all global symbols + std::vector<const ld::Atom*>& globalAtoms = this->_writer._exportedAtoms; + _globals.reserve(globalAtoms.size()); + this->_writer._globalSymbolsStartIndex = symbolIndex; + for (std::vector<const ld::Atom*>::const_iterator it=globalAtoms.begin(); it != globalAtoms.end(); ++it) { + const ld::Atom* atom = *it; + this->addGlobal(atom, this->_writer._stringPoolAtom); + this->_writer._atomToSymbolIndex[atom] = symbolIndex++; + } + this->_writer._globalSymbolsCount = symbolIndex - this->_writer._globalSymbolsStartIndex; + + // make nlist entries for all undefined (imported) symbols + std::vector<const ld::Atom*>& importAtoms = this->_writer._importedAtoms; + _imports.reserve(importAtoms.size()); + this->_writer._importSymbolsStartIndex = symbolIndex; + for (std::vector<const ld::Atom*>::const_iterator it=importAtoms.begin(); it != importAtoms.end(); ++it) { + this->addImport(*it, this->_writer._stringPoolAtom); + this->_writer._atomToSymbolIndex[*it] = symbolIndex++; + } + this->_writer._importSymbolsCount = symbolIndex - this->_writer._importSymbolsStartIndex; +} + +template <typename A> +uint64_t SymbolTableAtom<A>::size() const +{ + return sizeof(macho_nlist<P>) * (_locals.size() + _globals.size() + _imports.size()); +} + +template <typename A> +void SymbolTableAtom<A>::copyRawContent(uint8_t buffer[]) const +{ + memcpy(&buffer[this->_writer._localSymbolsStartIndex*sizeof(macho_nlist<P>)], &_locals[0], + this->_writer._localSymbolsCount*sizeof(macho_nlist<P>)); + memcpy(&buffer[this->_writer._globalSymbolsStartIndex*sizeof(macho_nlist<P>)], &_globals[0], + this->_writer._globalSymbolsCount*sizeof(macho_nlist<P>)); + memcpy(&buffer[this->_writer._importSymbolsStartIndex *sizeof(macho_nlist<P>)], &_imports[0], + this->_writer._importSymbolsCount*sizeof(macho_nlist<P>)); +} + + + + +class RelocationsAtomAbstract : public ClassicLinkEditAtom +{ +public: + RelocationsAtomAbstract(const Options& opts, ld::Internal& state, + OutputFile& writer, const ld::Section& sect, + unsigned int pointerSize) + : ClassicLinkEditAtom(opts, state, writer, sect, pointerSize) { } + + virtual void addPointerReloc(uint64_t addr, uint32_t symNum) = 0; + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) = 0; + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) = 0; + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) = 0; + virtual uint64_t relocBaseAddress(ld::Internal& state) = 0; + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) = 0; +protected: + uint32_t symbolIndex(const ld::Atom* atom) const; + +}; + + + +uint32_t RelocationsAtomAbstract::symbolIndex(const ld::Atom* atom) const +{ + std::map<const ld::Atom*, uint32_t>::iterator pos = this->_writer._atomToSymbolIndex.find(atom); + if ( pos != this->_writer._atomToSymbolIndex.end() ) + return pos->second; + fprintf(stderr, "_atomToSymbolIndex content:\n"); + for(std::map<const ld::Atom*, uint32_t>::iterator it = this->_writer._atomToSymbolIndex.begin(); it != this->_writer._atomToSymbolIndex.end(); ++it) { + fprintf(stderr, "%p(%s) => %d\n", it->first, it->first->name(), it->second); + } + throwf("internal error: atom not found in symbolIndex(%s)", atom->name()); +} + + +template <typename A> +class LocalRelocationsAtom : public RelocationsAtomAbstract +{ +public: + LocalRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "local relocations"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode() {} + // overrides of RelocationsAtomAbstract + virtual void addPointerReloc(uint64_t addr, uint32_t symNum); + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) {} + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) {} + virtual uint64_t relocBaseAddress(ld::Internal& state); + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum); + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) { } + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + std::vector<macho_relocation_info<P> > _relocs; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section LocalRelocationsAtom<A>::_s_section("__LINKEDIT", "__local_relocs", ld::Section::typeLinkEdit, true); + + +template <> +uint64_t LocalRelocationsAtom<x86_64>::relocBaseAddress(ld::Internal& state) +{ + if ( _options.outputKind() == Options::kKextBundle ) { + // for kext bundles the reloc base address starts at __TEXT segment + return _options.baseAddress(); + } + // for all other kinds, the x86_64 reloc base address starts at __DATA segment + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + return sect->address; + } + throw "__DATA segment not found"; +} + +template <typename A> +uint64_t LocalRelocationsAtom<A>::relocBaseAddress(ld::Internal& state) +{ + return _options.baseAddress(); +} + +template <typename A> +void LocalRelocationsAtom<A>::addPointerReloc(uint64_t addr, uint32_t symNum) +{ + macho_relocation_info<P> reloc; + reloc.set_r_address(addr); + reloc.set_r_symbolnum(symNum); + reloc.set_r_pcrel(false); + reloc.set_r_length(); + reloc.set_r_extern(false); + reloc.set_r_type(GENERIC_RELOC_VANILLA); + _relocs.push_back(reloc); +} + +template <typename A> +void LocalRelocationsAtom<A>::addTextReloc(uint64_t addr, ld::Fixup::Kind kind, uint64_t targetAddr, uint32_t symNum) +{ + macho_relocation_info<P> reloc1; + macho_relocation_info<P> reloc2; + switch ( kind ) { + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + // a reference to the absolute address of something in this same linkage unit can be + // encoded as a local text reloc in a dylib or bundle + if ( _options.outputSlidable() ) { + reloc1.set_r_address(addr); + reloc1.set_r_symbolnum(symNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + reloc2.set_r_address(targetAddr >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + _relocs.push_back(reloc1); + _relocs.push_back(reloc2); + } + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCAbsHigh16: + if ( _options.outputSlidable() ) { + reloc1.set_r_address(addr); + reloc1.set_r_symbolnum(symNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(false); + reloc1.set_r_type(kind==ld::Fixup::kindStorePPCAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); + reloc2.set_r_address(targetAddr & 0xFFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + _relocs.push_back(reloc1); + _relocs.push_back(reloc2); + } + break; + default: + break; + } +} + + +template <typename A> +uint64_t LocalRelocationsAtom<A>::size() const +{ + return _relocs.size() * sizeof(macho_relocation_info<P>); +} + +template <typename A> +void LocalRelocationsAtom<A>::copyRawContent(uint8_t buffer[]) const +{ + memcpy(buffer, &_relocs[0], _relocs.size()*sizeof(macho_relocation_info<P>)); +} + + + + + + +template <typename A> +class ExternalRelocationsAtom : public RelocationsAtomAbstract +{ +public: + ExternalRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "external relocations"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode() {} + // overrides of RelocationsAtomAbstract + virtual void addPointerReloc(uint64_t addr, uint32_t symNum) {} + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) {} + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*); + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*); + virtual uint64_t relocBaseAddress(ld::Internal& state); + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) { } + + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct LocAndAtom { + LocAndAtom(uint64_t l, const ld::Atom* a) : loc(l), atom(a), symbolIndex(0) {} + + uint64_t loc; + const ld::Atom* atom; + uint32_t symbolIndex; + + bool operator<(const LocAndAtom& rhs) const { + // sort first by symbol number + if ( this->symbolIndex != rhs.symbolIndex ) + return (this->symbolIndex < rhs.symbolIndex); + // then sort all uses of the same symbol by address + return (this->loc < rhs.loc); + } + + }; + + static uint32_t pointerReloc(); + static uint32_t callReloc(); + + mutable std::vector<LocAndAtom> _pointerLocations; + mutable std::vector<LocAndAtom> _callSiteLocations; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section ExternalRelocationsAtom<A>::_s_section("__LINKEDIT", "__extrn_relocs", ld::Section::typeLinkEdit, true); + +template <> +uint64_t ExternalRelocationsAtom<x86_64>::relocBaseAddress(ld::Internal& state) +{ + // for x86_64 the reloc base address starts at __DATA segment + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->segmentName(), "__DATA") == 0 ) + return sect->address; + } + throw "__DATA segment not found"; +} + +template <typename A> +uint64_t ExternalRelocationsAtom<A>::relocBaseAddress(ld::Internal& state) +{ + return 0; +} + +template <typename A> +void ExternalRelocationsAtom<A>::addExternalPointerReloc(uint64_t addr, const ld::Atom* target) +{ + _pointerLocations.push_back(LocAndAtom(addr, target)); +} + +template <typename A> +void ExternalRelocationsAtom<A>::addExternalCallSiteReloc(uint64_t addr, const ld::Atom* target) +{ + _callSiteLocations.push_back(LocAndAtom(addr, target)); +} + + +template <typename A> +uint64_t ExternalRelocationsAtom<A>::size() const +{ + if ( _options.outputKind() == Options::kStaticExecutable ) { + assert(_pointerLocations.size() == 0); + assert(_callSiteLocations.size() == 0); + } + return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info<P>); +} + +template <> uint32_t ExternalRelocationsAtom<arm>::pointerReloc() { return ARM_RELOC_VANILLA; } +template <> uint32_t ExternalRelocationsAtom<x86>::pointerReloc() { return GENERIC_RELOC_VANILLA; } +template <> uint32_t ExternalRelocationsAtom<ppc>::pointerReloc() { return PPC_RELOC_VANILLA; } +template <> uint32_t ExternalRelocationsAtom<x86_64>::pointerReloc() { return X86_64_RELOC_UNSIGNED; } +template <> uint32_t ExternalRelocationsAtom<ppc64>::pointerReloc() { return PPC_RELOC_VANILLA; } + + +template <> uint32_t ExternalRelocationsAtom<x86_64>::callReloc() { return X86_64_RELOC_BRANCH; } +template <> uint32_t ExternalRelocationsAtom<x86>::callReloc() { return GENERIC_RELOC_VANILLA; } +template <typename A> +uint32_t ExternalRelocationsAtom<A>::callReloc() +{ + assert(0 && "external call relocs not implemented"); + return 0; +} + + +template <typename A> +void ExternalRelocationsAtom<A>::copyRawContent(uint8_t buffer[]) const +{ + macho_relocation_info<P>* r = (macho_relocation_info<P>*)buffer; + + // assign symbol index, now that symbol table is built + for (typename std::vector<LocAndAtom>::iterator it = _pointerLocations.begin(); it != _pointerLocations.end(); ++it) { + it->symbolIndex = symbolIndex(it->atom); + } + std::sort(_pointerLocations.begin(), _pointerLocations.end()); + for (typename std::vector<LocAndAtom>::const_iterator it = _pointerLocations.begin(); it != _pointerLocations.end(); ++it, ++r) { + r->set_r_address(it->loc); + r->set_r_symbolnum(it->symbolIndex); + r->set_r_pcrel(false); + r->set_r_length(); + r->set_r_extern(true); + r->set_r_type(this->pointerReloc()); + } + + for (typename std::vector<LocAndAtom>::iterator it = _callSiteLocations.begin(); it != _callSiteLocations.end(); ++it) { + it->symbolIndex = symbolIndex(it->atom); + } + std::sort(_callSiteLocations.begin(), _callSiteLocations.end()); + for (typename std::vector<LocAndAtom>::const_iterator it = _callSiteLocations.begin(); it != _callSiteLocations.end(); ++it, ++r) { + r->set_r_address(it->loc); + r->set_r_symbolnum(it->symbolIndex); + r->set_r_pcrel(true); + r->set_r_length(2); + r->set_r_extern(true); + r->set_r_type(this->callReloc()); + } +} + + +template <typename A> +class SectionRelocationsAtom : public RelocationsAtomAbstract +{ +public: + SectionRelocationsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : RelocationsAtomAbstract(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "section relocations"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode(); + // overrides of RelocationsAtomAbstract + virtual void addPointerReloc(uint64_t addr, uint32_t symNum) {} + virtual void addTextReloc(uint64_t addr, ld::Fixup::Kind k, uint64_t targetAddr, uint32_t symNum) {} + virtual void addExternalPointerReloc(uint64_t addr, const ld::Atom*) {} + virtual void addExternalCallSiteReloc(uint64_t addr, const ld::Atom*) {} + virtual uint64_t relocBaseAddress(ld::Internal& state) { return 0; } + virtual void addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + + struct Entry { + ld::Fixup::Kind kind; + bool toTargetUsesExternalReloc; + bool fromTargetUsesExternalReloc; + const ld::Atom* inAtom; + uint32_t offsetInAtom; + const ld::Atom* toTarget; + uint64_t toAddend; + const ld::Atom* fromTarget; + uint64_t fromAddend; + }; + uint32_t sectSymNum(bool external, const ld::Atom* target); + void encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector<macho_relocation_info<P> >& relocs); + + struct SectionAndEntries { + ld::Internal::FinalSection* sect; + std::vector<Entry> entries; + std::vector<macho_relocation_info<P> > relocs; + }; + + std::vector<SectionAndEntries> _entriesBySection; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section SectionRelocationsAtom<A>::_s_section("__LINKEDIT", "__sect_relocs", ld::Section::typeLinkEdit, true); + + + + +template <typename A> +uint64_t SectionRelocationsAtom<A>::size() const +{ + uint32_t count = 0; + for(typename std::vector<SectionAndEntries>::const_iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + const SectionAndEntries& se = *it; + count += se.relocs.size(); + } + return count * sizeof(macho_relocation_info<P>); +} + +template <typename A> +void SectionRelocationsAtom<A>::copyRawContent(uint8_t buffer[]) const +{ + uint32_t offset = 0; + for(typename std::vector<SectionAndEntries>::const_iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + const SectionAndEntries& se = *it; + memcpy(&buffer[offset], &se.relocs[0], se.relocs.size()*sizeof(macho_relocation_info<P>)); + offset += (se.relocs.size() * sizeof(macho_relocation_info<P>)); + } +} + + +template <> +void SectionRelocationsAtom<x86_64>::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector<macho_relocation_info<P> >& relocs) +{ + macho_relocation_info<P> reloc1; + macho_relocation_info<P> reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86BranchPCRel8: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(0); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_BRANCH); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32_1: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_1); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32_2: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_2); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32_4: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_SIGNED_4); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel32GOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_GOT); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(X86_64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + default: + assert(0 && "need to handle -r reloc"); + + } + +} + + + +template <typename A> +uint32_t SectionRelocationsAtom<A>::sectSymNum(bool external, const ld::Atom* target) +{ + if ( target->definition() == ld::Atom::definitionAbsolute ) + return R_ABS; + if ( external ) + return this->symbolIndex(target); // in external relocations, r_symbolnum field is symbol index + else + return target->machoSection(); // in non-extern relocations, r_symbolnum is mach-o section index of target +} + +template <> +void SectionRelocationsAtom<x86>::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector<macho_relocation_info<P> >& relocs) +{ + macho_relocation_info<P> reloc1; + macho_relocation_info<P> reloc2; + macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1; + macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86BranchPCRel8: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(0); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(0); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreX86PCRel16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(1); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(1); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(GENERIC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + default: + assert(0 && "need to handle -r reloc"); + + } +} + + +template <> +void SectionRelocationsAtom<arm>::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector<macho_relocation_info<P> >& relocs) +{ + macho_relocation_info<P> reloc1; + macho_relocation_info<P> reloc2; + macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1; + macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_THUMB_RELOC_BR22); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM_THUMB_RELOC_BR22); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(ARM_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(ARM_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + //unsigned int pcBaseOffset = entry.inAtom->isThumb() ? 4 : 8; + //if ( entry.fromAddend > pcBaseOffset ) + // sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend-pcBaseOffset); + //else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else { + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + } + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(ARM_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreARMLow16: + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreThumbLow16: + case ld::Fixup::kindStoreThumbHigh16: + { + int len = 0; + uint32_t otherHalf = 0; + uint32_t value = entry.toTarget->finalAddress()+entry.toAddend; + if ( entry.fromTarget != NULL ) + value -= (entry.fromTarget->finalAddress()+entry.fromAddend); + switch ( entry.kind ) { + case ld::Fixup::kindStoreARMLow16: + len = 0; + otherHalf = value >> 16; + break; + case ld::Fixup::kindStoreARMHigh16: + len = 1; + otherHalf = value & 0xFFFF; + break; + case ld::Fixup::kindStoreThumbLow16: + len = 2; + otherHalf = value >> 16; + break; + case ld::Fixup::kindStoreThumbHigh16: + len = 3; + otherHalf = value & 0xFFFF; + break; + default: + break; + } + if ( entry.fromTarget != NULL ) { + // this is a sect-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(len); + sreloc1->set_r_type(ARM_RELOC_HALF_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(len); + sreloc2->set_r_type(ARM_RELOC_PAIR); + sreloc2->set_r_address(otherHalf); + if ( entry.fromTarget == entry.inAtom ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // this is absolute address + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(len); + sreloc1->set_r_type(ARM_RELOC_HALF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + reloc2.set_r_address(otherHalf); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(len); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(len); + reloc1.set_r_extern(false); + reloc1.set_r_type(ARM_RELOC_HALF); + reloc2.set_r_address(otherHalf); // other half + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(len); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + } + } + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} + + +template <> +void SectionRelocationsAtom<ppc>::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector<macho_relocation_info<P> >& relocs) +{ + macho_relocation_info<P> reloc1; + macho_relocation_info<P> reloc2; + macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1; + macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); + } + uint32_t toAddr; + uint32_t fromAddr; + + switch ( entry.kind ) { + + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStorePPCBranch14: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR14); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + if ( external ) + reloc2.set_r_address(entry.toAddend >> 16); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HI16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HA16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicHigh16AddLow: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} + +template <> +void SectionRelocationsAtom<ppc64>::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector<macho_relocation_info<P> >& relocs) +{ + macho_relocation_info<P> reloc1; + macho_relocation_info<P> reloc2; + macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1; + macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum= sectSymNum(fromExternal, entry.fromTarget); + } + uint32_t toAddr; + uint32_t fromAddr; + + switch ( entry.kind ) { + + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR24); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR24); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStorePPCBranch14: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(true); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_BR14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_BR14); + } + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreBigEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(3); + if ( entry.toTarget->scope() == ld::Atom::scopeTranslationUnit ) + sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); + else + sreloc1->set_r_type(PPC_RELOC_SECTDIFF); + sreloc1->set_r_address(address); + if ( entry.toTarget == entry.inAtom ) + sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend); + else + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(3); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(0); + if ( entry.fromTarget == entry.inAtom ) { + if ( entry.fromAddend > entry.fromTarget->size() ) + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.offsetInAtom); + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()+entry.fromAddend); + } + else + sreloc2->set_r_value(entry.fromTarget->finalAddress()); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + } + else { + // regular pointer + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc is target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(3); + sreloc1->set_r_type(GENERIC_RELOC_VANILLA); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(GENERIC_RELOC_VANILLA); + } + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(entry.kind==ld::Fixup::kindStorePPCAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); + } + if ( external ) + reloc2.set_r_address(entry.toAddend >> 16); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) >> 16); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HI16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HI16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + if ( !external && (entry.toAddend != 0) ) { + // use scattered reloc if target offset is non-zero + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + } + else { + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(PPC_RELOC_HA16); + } + if ( external ) + reloc2.set_r_address(entry.toAddend & 0x0000FFFF); + else + reloc2.set_r_address((entry.toTarget->finalAddress()+entry.toAddend) & 0x0000FFFF); + reloc2.set_r_symbolnum(0); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(PPC_RELOC_PAIR); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(entry.kind == ld::Fixup::kindStorePPCPicLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + case ld::Fixup::kindStorePPCPicHigh16AddLow: + fromAddr = entry.fromTarget->finalAddress() + entry.fromAddend; + toAddr = entry.toTarget->finalAddress() + entry.toAddend; + sreloc1->set_r_scattered(true); + sreloc1->set_r_pcrel(false); + sreloc1->set_r_length(2); + sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); + sreloc1->set_r_address(address); + sreloc1->set_r_value(entry.toTarget->finalAddress()); + sreloc2->set_r_scattered(true); + sreloc2->set_r_pcrel(false); + sreloc2->set_r_length(2); + sreloc2->set_r_type(PPC_RELOC_PAIR); + sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); + sreloc2->set_r_value(fromAddr); + relocs.push_back(reloc1); + relocs.push_back(reloc2); + break; + + default: + assert(0 && "need to handle -r reloc"); + + } +} + +template <typename A> +void SectionRelocationsAtom<A>::addSectionReloc(ld::Internal::FinalSection* sect, ld::Fixup::Kind kind, + const ld::Atom* inAtom, uint32_t offsetInAtom, + bool toTargetUsesExternalReloc ,bool fromTargetExternalReloc, + const ld::Atom* toTarget, uint64_t toAddend, + const ld::Atom* fromTarget, uint64_t fromAddend) +{ + Entry entry; + entry.kind = kind; + entry.toTargetUsesExternalReloc = toTargetUsesExternalReloc; + entry.fromTargetUsesExternalReloc = fromTargetExternalReloc; + entry.inAtom = inAtom; + entry.offsetInAtom = offsetInAtom; + entry.toTarget = toTarget; + entry.toAddend = toAddend; + entry.fromTarget = fromTarget; + entry.fromAddend = fromAddend; + + static ld::Internal::FinalSection* lastSection = NULL; + static SectionAndEntries* lastSectionAndEntries = NULL; + + if ( sect != lastSection ) { + for(typename std::vector<SectionAndEntries>::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + if ( sect == it->sect ) { + lastSection = sect; + lastSectionAndEntries = &*it; + break; + } + } + if ( sect != lastSection ) { + SectionAndEntries tmp; + tmp.sect = sect; + _entriesBySection.push_back(tmp); + lastSection = sect; + lastSectionAndEntries = &_entriesBySection.back(); + } + } + lastSectionAndEntries->entries.push_back(entry); +} + +template <typename A> +void SectionRelocationsAtom<A>::encode() +{ + // convert each Entry record to one or two reloc records + for(typename std::vector<SectionAndEntries>::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + SectionAndEntries& se = *it; + for(typename std::vector<Entry>::iterator eit=se.entries.begin(); eit != se.entries.end(); ++eit) { + encodeSectionReloc(se.sect, *eit, se.relocs); + } + } + + // update sections with start and count or relocs + uint32_t index = 0; + for(typename std::vector<SectionAndEntries>::iterator it=_entriesBySection.begin(); it != _entriesBySection.end(); ++it) { + SectionAndEntries& se = *it; + se.sect->relocStart = index; + se.sect->relocCount = se.relocs.size(); + index += se.sect->relocCount; + } + +} + + + +template <typename A> +class IndirectSymbolTableAtom : public ClassicLinkEditAtom +{ +public: + IndirectSymbolTableAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : ClassicLinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "indirect symbol table"; } + virtual uint64_t size() const; + virtual void copyRawContent(uint8_t buffer[]) const; + // overrides of ClassicLinkEditAtom + virtual void encode(); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + void encodeStubSection(ld::Internal::FinalSection* sect); + void encodeLazyPointerSection(ld::Internal::FinalSection* sect); + void encodeNonLazyPointerSection(ld::Internal::FinalSection* sect); + uint32_t symIndexOfStubAtom(const ld::Atom*); + uint32_t symIndexOfLazyPointerAtom(const ld::Atom*); + uint32_t symIndexOfNonLazyPointerAtom(const ld::Atom*); + uint32_t symbolIndex(const ld::Atom*); + bool kextBundlesDontHaveIndirectSymbolTable(); + + + std::vector<uint32_t> _entries; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section IndirectSymbolTableAtom<A>::_s_section("__LINKEDIT", "__ind_sym_tab", ld::Section::typeLinkEdit, true); + + + + +template <typename A> +uint32_t IndirectSymbolTableAtom<A>::symbolIndex(const ld::Atom* atom) +{ + std::map<const ld::Atom*, uint32_t>::iterator pos = this->_writer._atomToSymbolIndex.find(atom); + if ( pos != this->_writer._atomToSymbolIndex.end() ) + return pos->second; + //fprintf(stderr, "_atomToSymbolIndex content:\n"); + //for(std::map<const ld::Atom*, uint32_t>::iterator it = this->_writer._atomToSymbolIndex.begin(); it != this->_writer._atomToSymbolIndex.end(); ++it) { + // fprintf(stderr, "%p(%s) => %d\n", it->first, it->first->name(), it->second); + //} + throwf("internal error: atom not found in symbolIndex(%s)", atom->name()); +} + +template <typename A> +uint32_t IndirectSymbolTableAtom<A>::symIndexOfStubAtom(const ld::Atom* stubAtom) +{ + for (ld::Fixup::iterator fit = stubAtom->fixupsBegin(); fit != stubAtom->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + assert((fit->u.target->contentType() == ld::Atom::typeLazyPointer) + || (fit->u.target->contentType() == ld::Atom::typeLazyDylibPointer)); + return symIndexOfLazyPointerAtom(fit->u.target); + } + } + throw "internal error: stub missing fixup to lazy pointer"; +} + + +template <typename A> +uint32_t IndirectSymbolTableAtom<A>::symIndexOfLazyPointerAtom(const ld::Atom* lpAtom) +{ + for (ld::Fixup::iterator fit = lpAtom->fixupsBegin(); fit != lpAtom->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindLazyTarget ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + return symbolIndex(fit->u.target); + } + } + throw "internal error: lazy pointer missing fixupLazyTarget fixup"; +} + +template <typename A> +uint32_t IndirectSymbolTableAtom<A>::symIndexOfNonLazyPointerAtom(const ld::Atom* nlpAtom) +{ + //fprintf(stderr, "symIndexOfNonLazyPointerAtom(%p) %s\n", nlpAtom, nlpAtom->name()); + for (ld::Fixup::iterator fit = nlpAtom->fixupsBegin(); fit != nlpAtom->fixupsEnd(); ++fit) { + // non-lazy-pointer to a stripped symbol => no symbol index + if ( fit->clusterSize != ld::Fixup::k1of1 ) + return INDIRECT_SYMBOL_LOCAL; + const ld::Atom* target; + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = _state.indirectBindingTable[fit->u.bindingIndex]; + break; + default: + throw "internal error: unexpected non-lazy pointer binding"; + } + // Special case non-lazy-pointer slot used to point to "dyld_stub_binder" + // That slot is never bound using indirect symbol table + if ( target == _state.compressedFastBinderProxy ) + return INDIRECT_SYMBOL_ABS; + bool targetIsGlobal = (target->scope() == ld::Atom::scopeGlobal); + switch ( target->definition() ) { + case ld::Atom::definitionRegular: + if ( targetIsGlobal ) { + if ( _options.outputKind() == Options::kObjectFile ) { + // nlpointer to global symbol uses indirect symbol table in .o files + return symbolIndex(target); + } + else if ( target->combine() == ld::Atom::combineByName ) { + // dyld needs to bind nlpointer to global weak def + return symbolIndex(target); + } + else if ( _options.nameSpace() != Options::kTwoLevelNameSpace ) { + // dyld needs to bind nlpointer to global def linked for flat namespace + return symbolIndex(target); + } + } + break; + case ld::Atom::definitionTentative: + case ld::Atom::definitionAbsolute: + if ( _options.outputKind() == Options::kObjectFile ) { + // tentative def in .o file always uses symbol index + return symbolIndex(target); + } + // dyld needs to bind nlpointer to global def linked for flat namespace + if ( targetIsGlobal && _options.nameSpace() != Options::kTwoLevelNameSpace ) + return symbolIndex(target); + break; + case ld::Atom::definitionProxy: + // dyld needs to bind nlpointer to something in another dylib + { + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal data reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + } + return symbolIndex(target); + } + } + if ( nlpAtom->fixupsBegin() == nlpAtom->fixupsEnd() ) { + // no fixups means this is the ImageLoader cache slot + return INDIRECT_SYMBOL_ABS; + } + + // The magic index INDIRECT_SYMBOL_LOCAL tells dyld it should does not need to bind + // this non-lazy pointer. + return INDIRECT_SYMBOL_LOCAL; +} + + + +template <typename A> +void IndirectSymbolTableAtom<A>::encodeStubSection(ld::Internal::FinalSection* sect) +{ + sect->indirectSymTabStartIndex = _entries.size(); + sect->indirectSymTabElementSize = sect->atoms[0]->size(); + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + _entries.push_back(symIndexOfStubAtom(*ait)); + } +} + +template <typename A> +void IndirectSymbolTableAtom<A>::encodeLazyPointerSection(ld::Internal::FinalSection* sect) +{ + sect->indirectSymTabStartIndex = _entries.size(); + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + _entries.push_back(symIndexOfLazyPointerAtom(*ait)); + } +} + +template <typename A> +void IndirectSymbolTableAtom<A>::encodeNonLazyPointerSection(ld::Internal::FinalSection* sect) +{ + sect->indirectSymTabStartIndex = _entries.size(); + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + _entries.push_back(symIndexOfNonLazyPointerAtom(*ait)); + } +} + +template <typename A> +bool IndirectSymbolTableAtom<A>::kextBundlesDontHaveIndirectSymbolTable() +{ + return true; +} + +template <typename A> +void IndirectSymbolTableAtom<A>::encode() +{ + // static executables should not have an indirect symbol table + if ( this->_options.outputKind() == Options::kStaticExecutable ) + return; + + // x86_64 kext bundles should not have an indirect symbol table + if ( (this->_options.outputKind() == Options::kKextBundle) && kextBundlesDontHaveIndirectSymbolTable() ) + return; + + // find all special sections that need a range of the indirect symbol table section + for (std::vector<ld::Internal::FinalSection*>::iterator sit = this->_state.sections.begin(); sit != this->_state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + switch ( sect->type() ) { + case ld::Section::typeStub: + case ld::Section::typeStubClose: + this->encodeStubSection(sect); + break; + case ld::Section::typeLazyPointerClose: + case ld::Section::typeLazyPointer: + case ld::Section::typeLazyDylibPointer: + this->encodeLazyPointerSection(sect); + break; + case ld::Section::typeNonLazyPointer: + this->encodeNonLazyPointerSection(sect); + break; + default: + break; + } + } +} + +template <typename A> +uint64_t IndirectSymbolTableAtom<A>::size() const +{ + return _entries.size() * sizeof(uint32_t); +} + +template <typename A> +void IndirectSymbolTableAtom<A>::copyRawContent(uint8_t buffer[]) const +{ + uint32_t* array = (uint32_t*)buffer; + for(unsigned long i=0; i < _entries.size(); ++i) { + E::set32(array[i], _entries[i]); + } +} + + + + + + + + +} // namespace tool +} // namespace ld + +#endif // __LINKEDIT_CLASSIC_HPP__ diff --git a/src/ld/MachOReaderDylib.hpp b/src/ld/MachOReaderDylib.hpp deleted file mode 100644 index 5a6c553..0000000 --- a/src/ld/MachOReaderDylib.hpp +++ /dev/null @@ -1,1048 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_DYLIB_MACH_O__ -#define __OBJECT_FILE_DYLIB_MACH_O__ - -#include <stdint.h> -#include <math.h> -#include <unistd.h> -#include <sys/param.h> - - -#include <vector> -#include <set> -#include <algorithm> -#include <ext/hash_map> - -#include "MachOFileAbstraction.hpp" -#include "MachOTrie.hpp" -#include "ObjectFile.h" - -// -// -// To implement architecture xxx, you must write template specializations for the following method: -// Reader<xxx>::validFile() -// -// - - - - -namespace mach_o { -namespace dylib { - - -// forward reference -template <typename A> class Reader; - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name) { fName = name; } - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return false; } -private: - const char* fName; -}; - - -// -// An ExportAtom has no content. It exists so that the linker can track which imported -// symbols came from which dynamic libraries. -// -template <typename A> -class ExportAtom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const { return fName; } - virtual Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } - virtual DefinitionKind getDefinitionKind() const { return fWeakDefinition ? kExternalWeakDefinition : kExternalDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual bool dontDeadStrip() const { return false; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return "._imports"; } - virtual Segment& getSegment() const { return fgImportSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector<ObjectFile::LineInfo>* 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<A>; - typedef typename A::P P; - - ExportAtom(ObjectFile::Reader& owner, const char* name, bool weak, uint32_t ordinal) - : fOwner(owner), fName(name), fOrdinal(ordinal), fWeakDefinition(weak) {} - virtual ~ExportAtom() {} - - ObjectFile::Reader& fOwner; - const char* fName; - uint32_t fOrdinal; - bool fWeakDefinition; - - static std::vector<ObjectFile::Reference*> fgEmptyReferenceList; - static Segment fgImportSegment; -}; - -template <typename A> -Segment ExportAtom<A>::fgImportSegment("__LINKEDIT"); - -template <typename A> -std::vector<ObjectFile::Reference*> ExportAtom<A>::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 <typename A> -class ImportAtom : public ObjectFile::Atom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return "flat-imports"; } - virtual const char* getDisplayName() const { return "flat_namespace undefines"; } - virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return false; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(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<ObjectFile::LineInfo>* 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<A>; - typedef typename A::P P; - - ImportAtom(ObjectFile::Reader& owner, uint32_t ordinal, std::vector<const char*>& imports) - : fOwner(owner), fOrdinal(ordinal) { makeReferences(imports); } - virtual ~ImportAtom() {} - void makeReferences(std::vector<const char*>& imports) { - for (std::vector<const char*>::iterator it=imports.begin(); it != imports.end(); ++it) { - fReferences.push_back(new ImportReference(*it)); - } - } - - - ObjectFile::Reader& fOwner; - uint32_t fOrdinal; - std::vector<ObjectFile::Reference*> fReferences; - - static Segment fgImportSegment; -}; - -template <typename A> -Segment ImportAtom<A>::fgImportSegment("__LINKEDIT"); - - - - -// -// The reader for a dylib extracts all exported symbols names from the memory-mapped -// dylib, builds a hash table, then unmaps the file. This is an important memory -// savings for large dylibs. -// -template <typename A> -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, bool executableOrDylib); - Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const LibraryOptions& dylibOptions, const ObjectFile::ReaderOptions& options, - uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector<class ObjectFile::Atom*>& getAtoms(); - virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name); - virtual std::vector<Stab>* getStabs() { return NULL; } - virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjcContraint; } - virtual const char* getInstallPath() { return fDylibInstallPath; } - virtual uint32_t getTimestamp() { return fDylibTimeStamp; } - virtual uint32_t getCurrentVersion() { return fDylibtCurrentVersion; } - virtual uint32_t getCompatibilityVersion() { return fDylibCompatibilityVersion; } - virtual void processIndirectLibraries(DylibHander* handler); - virtual void setExplicitlyLinked() { fExplicitlyLinked = true; } - virtual bool explicitlyLinked() { return fExplicitlyLinked; } - virtual bool implicitlyLinked() { return fImplicitlyLinked; } - virtual bool providedExportAtom() { return fProvidedAtom; } - virtual const char* parentUmbrella() { return fParentUmbrella; } - virtual std::vector<const char*>* getAllowableClients(); - virtual bool hasWeakExternals() { return fHasWeakExports; } - virtual bool deadStrippable() { return fDeadStrippable; } - virtual bool isLazyLoadedDylib() { return fLazyLoaded; } - - virtual void setImplicitlyLinked() { fImplicitlyLinked = true; } - -protected: - - struct ReExportChain { ReExportChain* prev; Reader<A>* reader; }; - - void assertNoReExportCycles(ReExportChain*); - -private: - typedef typename A::P P; - typedef typename A::P::E E; - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - struct AtomAndWeak { ObjectFile::Atom* atom; bool weak; uint32_t ordinal; }; - typedef __gnu_cxx::hash_map<const char*, AtomAndWeak, __gnu_cxx::hash<const char*>, CStringEquals> NameToAtomMap; - typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet; - typedef typename NameToAtomMap::iterator NameToAtomMapIterator; - - struct PathAndFlag { const char* path; bool reExport; }; - - bool isPublicLocation(const char* path); - void addSymbol(const char* name, bool weak); - void addDyldFastStub(); - void buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo, - const uint8_t* fileContent); - void buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo, - const macho_nlist<P>* symbolTable, const char* strings, - const uint8_t* fileContent); - - const char* fPath; - const char* fParentUmbrella; - std::vector<const char*> fAllowableClients; - const char* fDylibInstallPath; - uint32_t fDylibTimeStamp; - uint32_t fDylibtCurrentVersion; - uint32_t fDylibCompatibilityVersion; - uint32_t fReExportedOrdinal; - std::vector<PathAndFlag> fDependentLibraryPaths; - NameToAtomMap fAtoms; - NameSet fIgnoreExports; - bool fNoRexports; - bool fHasWeakExports; - bool fDeadStrippable; - const bool fLinkingFlat; - const bool fLinkingMainExecutable; - bool fExplictReExportFound; - bool fExplicitlyLinked; - bool fImplicitlyLinked; - bool fProvidedAtom; - bool fImplicitlyLinkPublicDylibs; - bool fLazyLoaded; - ObjectFile::Reader::ObjcConstraint fObjcContraint; - std::vector<ObjectFile::Reader*> fReExportedChildren; - const ObjectFile::ReaderOptions::MacVersionMin fMacDeploymentVersionMin; - const ObjectFile::ReaderOptions::IPhoneVersionMin fIPhoneDeploymentVersionMin; - std::vector<class ObjectFile::Atom*> fFlatImports; - - static bool fgLogHashtable; - static std::vector<class ObjectFile::Atom*> fgEmptyAtomList; -}; - -template <typename A> -std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyAtomList; -template <typename A> -bool Reader<A>::fgLogHashtable = false; - - -template <typename A> -Reader<A>::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, - const LibraryOptions& dylibOptions, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase) - : fParentUmbrella(NULL), fDylibInstallPath(NULL), fDylibTimeStamp(0), fDylibtCurrentVersion(0), - fDylibCompatibilityVersion(0), fReExportedOrdinal(ordinalBase), fLinkingFlat(options.fFlatNamespace), - fLinkingMainExecutable(options.fLinkingMainExecutable), fExplictReExportFound(false), - fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), - fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), - fObjcContraint(ObjectFile::Reader::kObjcNone), - fMacDeploymentVersionMin(options.fMacVersionMin), - fIPhoneDeploymentVersionMin(options.fIPhoneVersionMin) -{ - // sanity check - if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) - throw "not a valid mach-o object file"; - - fPath = strdup(path); - - const macho_header<P>* header = (const macho_header<P>*)fileContent; - const uint32_t cmd_count = header->ncmds(); - const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>)); - const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds()); - - // write out path for -whatsloaded option - if ( options.fLogAllFiles ) - printf("%s\n", path); - - if ( options.fRootSafe && ((header->flags() & MH_ROOT_SAFE) == 0) ) - warning("using -root_safe but linking against %s which is not root safe", path); - - if ( options.fSetuidSafe && ((header->flags() & MH_SETUID_SAFE) == 0) ) - warning("using -setuid_safe but linking against %s which is not setuid safe", path); - - // a "blank" stub has zero load commands - if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { - // no further processing needed - munmap((caddr_t)fileContent, fileLength); - return; - } - - - // optimize the case where we know there is no reason to look at indirect dylibs - fNoRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS); - fHasWeakExports = (header->flags() & MH_WEAK_DEFINES); - fDeadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); - bool trackDependentLibraries = !fNoRexports || options.fFlatNamespace; - - // pass 1 builds list of all dependent libraries - const macho_load_command<P>* cmd = cmds; - if ( trackDependentLibraries ) { - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_REEXPORT_DYLIB: - fExplictReExportFound = true; - // fall into next case - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - PathAndFlag entry; - entry.path = strdup(((struct macho_dylib_command<P>*)cmd)->name()); - entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); - fDependentLibraryPaths.push_back(entry); - break; - } - cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - } - - // pass 2 determines re-export info - const macho_dysymtab_command<P>* dynamicInfo = NULL; - const macho_dyld_info_command<P>* dyldInfo = NULL; - const macho_nlist<P>* symbolTable = NULL; - const char* strings = NULL; - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd; - symbolTable = (const macho_nlist<P>*)((char*)header + symtab->symoff()); - strings = (char*)header + symtab->stroff(); - } - break; - case LC_DYSYMTAB: - dynamicInfo = (macho_dysymtab_command<P>*)cmd; - break; - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: - dyldInfo = (macho_dyld_info_command<P>*)cmd; - break; - case LC_ID_DYLIB: - { - macho_dylib_command<P>* dylibID = (macho_dylib_command<P>*)cmd; - fDylibInstallPath = strdup(dylibID->name()); - fDylibTimeStamp = dylibID->timestamp(); - fDylibtCurrentVersion = dylibID->current_version(); - fDylibCompatibilityVersion = dylibID->compatibility_version(); - } - break; - case LC_SUB_UMBRELLA: - if ( trackDependentLibraries ) { - const char* frameworkLeafName = ((macho_sub_umbrella_command<P>*)cmd)->sub_umbrella(); - for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - const char* dylibName = it->path; - const char* lastSlash = strrchr(dylibName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) - it->reExport = true; - } - } - break; - case LC_SUB_LIBRARY: - if ( trackDependentLibraries) { - const char* dylibBaseName = ((macho_sub_library_command<P>*)cmd)->sub_library(); - for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - const char* dylibName = it->path; - const char* lastSlash = strrchr(dylibName, '/'); - const char* leafStart = &lastSlash[1]; - if ( lastSlash == NULL ) - leafStart = dylibName; - const char* firstDot = strchr(leafStart, '.'); - int len = strlen(leafStart); - if ( firstDot != NULL ) - len = firstDot - leafStart; - if ( strncmp(leafStart, dylibBaseName, len) == 0 ) - it->reExport = true; - } - } - break; - case LC_SUB_FRAMEWORK: - fParentUmbrella = strdup(((macho_sub_framework_command<P>*)cmd)->umbrella()); - break; - case macho_segment_command<P>::CMD: - // check for Objective-C info - if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), "__OBJC") == 0 ) { - const macho_segment_command<P>* segment = (macho_segment_command<P>*)cmd; - const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>)); - const macho_section<P>* const sectionsEnd = §ionsStart[segment->nsects()]; - for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( strcmp(sect->sectname(), "__image_info") == 0 ) { - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); - if ( (sect->size() >= 8) && (contents[0] == 0) ) { - uint32_t flags = E::get32(contents[1]); - if ( (flags & 4) == 4 ) - fObjcContraint = ObjectFile::Reader::kObjcGC; - else if ( (flags & 2) == 2 ) - fObjcContraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - else - fObjcContraint = ObjectFile::Reader::kObjcRetainRelease; - } - else if ( sect->size() > 0 ) { - warning("can't parse __OBJC/__image_info section in %s", fPath); - } - } - } - } - } - - cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - - // Process the rest of the commands here. - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SUB_CLIENT: - const char *temp = strdup(((macho_sub_client_command<P>*)cmd)->client()); - fAllowableClients.push_back(temp); - break; - } - cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); - } - - // validate minimal load commands - if ( (fDylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) - throwf("dylib %s missing LC_ID_DYLIB load command", path); - if ( symbolTable == NULL ) - throw "binary missing LC_SYMTAB load command"; - if ( dynamicInfo == NULL ) - throw "binary missing LC_DYSYMTAB load command"; - - // if linking flat and this is a flat dylib, create one atom that references all imported symbols - if ( fLinkingFlat && fLinkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { - std::vector<const char*> importNames; - importNames.reserve(dynamicInfo->nundefsym()); - const macho_nlist<P>* start = &symbolTable[dynamicInfo->iundefsym()]; - const macho_nlist<P>* end = &start[dynamicInfo->nundefsym()]; - for (const macho_nlist<P>* sym=start; sym < end; ++sym) { - importNames.push_back(&strings[sym->n_strx()]); - } - fFlatImports.push_back(new ImportAtom<A>(*this, fReExportedOrdinal++, importNames)); - } - - // build hash table - if ( dyldInfo != NULL ) - buildExportHashTableFromExportInfo(dyldInfo, fileContent); - else - buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); - - // special case libSystem - if ( (fDylibInstallPath != NULL) && (strcmp(fDylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) - addDyldFastStub(); - - // unmap file - munmap((caddr_t)fileContent, fileLength); -} - - -template <typename A> -void Reader<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo, - const macho_nlist<P>* symbolTable, const char* strings, - const uint8_t* fileContent) -{ - if ( dynamicInfo->tocoff() == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->getPath()); - const macho_nlist<P>* start = &symbolTable[dynamicInfo->iextdefsym()]; - const macho_nlist<P>* end = &start[dynamicInfo->nextdefsym()]; - fAtoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count - for (const macho_nlist<P>* sym=start; sym < end; ++sym) { - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0); - } - } - else { - int32_t count = dynamicInfo->ntoc(); - fAtoms.resize(count); // set initial bucket count - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->getPath()); - const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); - for (int32_t i = 0; i < count; ++i) { - const uint32_t index = E::get32(toc[i].symbol_index); - const macho_nlist<P>* sym = &symbolTable[index]; - this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0); - } - } -} - - -template <typename A> -void Reader<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo, - const uint8_t* fileContent) -{ - if ( fgLogHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->getPath()); - if ( dyldInfo->export_size() > 0 ) { - const uint8_t* start = fileContent + dyldInfo->export_off(); - const uint8_t* end = &start[dyldInfo->export_size()]; - std::vector<mach_o::trie::Entry> list; - parseTrie(start, end, list); - for (std::vector<mach_o::trie::Entry>::iterator it=list.begin(); it != list.end(); ++it) - this->addSymbol(it->name, it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); - } -} - - -template <> -void Reader<x86_64>::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false); -} - -template <> -void Reader<x86>::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false); -} - -// hack for bring up of iPhoneOS builds on SnowLeopard -template <> -void Reader<arm>::addDyldFastStub() -{ - addSymbol("dyld_stub_binder", false); -} - -template <typename A> -void Reader<A>::addDyldFastStub() -{ - // do nothing -} - -template <typename A> -void Reader<A>::addSymbol(const char* name, bool weakDef) -{ - //fprintf(stderr, "addSymbol() %s\n", name); - // symbols that start with $ld$ are meta-data to the static linker - // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib - if ( strncmp(name, "$ld$", 4) == 0 ) { - // $ld$ <action> $ <condition> $ <symbol-name> - const char* symAction = &name[4]; - const char* symCond = strchr(symAction, '$'); - if ( symCond != NULL ) { - if ( fMacDeploymentVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) { - ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset; - // ex: $ld$add$os10.6$_foo - if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { - switch ( symCond[6] - '0' ) { - case 0: - case 1: - symVersionCondition = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - symVersionCondition = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - symVersionCondition = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - symVersionCondition = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - symVersionCondition = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - symVersionCondition = ObjectFile::ReaderOptions::k10_6; - break; - } - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( fMacDeploymentVersionMin == symVersionCondition ) { - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); - fIgnoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->getPath()); - } - } - } - else { - warning("bad symbol name: %s in dylib %s", name, this->getPath()); - } - } - else { - warning("bad symbol version: %s in dylib %s", name, this->getPath()); - } - } - else if ( fIPhoneDeploymentVersionMin != ObjectFile::ReaderOptions::kMinIPhoneVersionUnset ) { - ObjectFile::ReaderOptions::IPhoneVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinIPhoneVersionUnset; - // ex: $ld$add$os3.1$_foo - if ( (strncmp(symCond, "$os", 3) == 0) && isdigit(symCond[3]) && (symCond[4] == '.') ) { - if ( (symCond[3] == '2') && (symCond[5] == '0') ) - symVersionCondition = ObjectFile::ReaderOptions::k2_0; - else if ( (symCond[3] == '2') && (symCond[5] == '1') ) - symVersionCondition = ObjectFile::ReaderOptions::k2_1; - else if ( (symCond[3] == '2') && (symCond[5] >= '2') ) - symVersionCondition = ObjectFile::ReaderOptions::k2_2; - else if ( (symCond[3] == '3') && (symCond[5] == '0') ) - symVersionCondition = ObjectFile::ReaderOptions::k3_0; - else if ( (symCond[3] == '3') && (symCond[5] == '1') ) - symVersionCondition = ObjectFile::ReaderOptions::k3_1; - else if ( (symCond[3] == '3') && (symCond[5] >= '2') ) - symVersionCondition = ObjectFile::ReaderOptions::k3_2; - else if ( (symCond[3] >= '4') ) - symVersionCondition = ObjectFile::ReaderOptions::k4_0; - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( fIPhoneDeploymentVersionMin == symVersionCondition ) { - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); - fIgnoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->getPath()); - } - } - } - else { - warning("bad symbol name: %s in dylib %s", name, this->getPath()); - } - } - else { - warning("bad symbol version: %s in dylib %s, symCond=%s", name, this->getPath(), symCond); - } - } - } - else { - warning("bad symbol condition: %s in dylib %s", name, this->getPath()); - } - } - - // add symbol as possible export if we are not supposed to ignore it - if ( fIgnoreExports.count(name) == 0 ) { - AtomAndWeak bucket; - bucket.atom = NULL; - bucket.weak = weakDef; - bucket.ordinal = fReExportedOrdinal++; - if ( fgLogHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->getPath()); - fAtoms[strdup(name)] = bucket; - } -} - - -template <typename A> -std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms() -{ - return fFlatImports; -} - - -template <typename A> -std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name) -{ - // if supposed to ignore this export, then pretend I don't have it - if ( fIgnoreExports.count(name) != 0 ) - return NULL; - - std::vector<class ObjectFile::Atom*>* 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<A>(*this, name, pos->second.weak, pos->second.ordinal); - fProvidedAtom = true; - if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->getPath()); - } - // return a vector of one atom - atoms = new std::vector<class ObjectFile::Atom*>; - atoms->push_back(pos->second.atom); - } - else { - if ( fgLogHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s\n", name, this->getPath()); - // look in children that I re-export - for (std::vector<ObjectFile::Reader*>::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<class ObjectFile::Atom*>* 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<A>* newAtom = new ExportAtom<A>(*this, name, isWeakDef, fReExportedOrdinal++); - fProvidedAtom = true; - atoms = new std::vector<class ObjectFile::Atom*>; - atoms->push_back(newAtom); - delete childAtoms; - return atoms; - } - } - } - return atoms; -} - - - -template <typename A> -bool Reader<A>::isPublicLocation(const char* path) -{ - // -no_implicit_dylibs disables this optimization - if ( ! fImplicitlyLinkPublicDylibs ) - return false; - - // /usr/lib is a public location - if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == NULL) ) - return true; - - // /System/Library/Frameworks/ is a public location - if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { - const char* frameworkDot = strchr(&path[27], '.'); - // but only top level framework - // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true - // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false - // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false - if ( frameworkDot != NULL ) { - int frameworkNameLen = frameworkDot - &path[27]; - if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) - return true; - } - } - - return false; -} - -template <typename A> -void Reader<A>::processIndirectLibraries(DylibHander* handler) -{ - if ( fLinkingFlat ) { - for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - handler->findDylib(it->path, this->getPath()); - } - } - else if ( fNoRexports ) { - // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do - } - else { - // two-level, might have re-exports - for (typename std::vector<PathAndFlag>::iterator it = fDependentLibraryPaths.begin(); it != fDependentLibraryPaths.end(); it++) { - if ( it->reExport ) { - //fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->getInstallPath(), it->path); - // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child - ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); - if ( isPublicLocation(child->getInstallPath()) ) { - // promote this child to be automatically added as a direct dependent if this already is - if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,child->getInstallPath()) == 0) ) { - //fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", child->getInstallPath()); - ((Reader<A>*)child)->setImplicitlyLinked(); - } - else if ( child->explicitlyLinked() || child->implicitlyLinked() ) { - //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); - } - else { - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - else { - // add all child's symbols to me - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - else if ( !fExplictReExportFound ) { - // see if child contains LC_SUB_FRAMEWORK with my name - ObjectFile::Reader* child = handler->findDylib(it->path, this->getPath()); - const char* parentUmbrellaName = ((Reader<A>*)child)->parentUmbrella(); - if ( parentUmbrellaName != NULL ) { - const char* parentName = this->getPath(); - const char* lastSlash = strrchr(parentName, '/'); - if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { - // add all child's symbols to me - fReExportedChildren.push_back(child); - //fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->getInstallPath(), it->path); - } - } - } - } - } - - // check for re-export cycles - ReExportChain chain; - chain.prev = NULL; - chain.reader = this; - this->assertNoReExportCycles(&chain); -} - -template <typename A> -void Reader<A>::assertNoReExportCycles(ReExportChain* prev) -{ - // recursively check my re-exported dylibs - ReExportChain chain; - chain.prev = prev; - chain.reader = this; - for (std::vector<ObjectFile::Reader*>::iterator it = fReExportedChildren.begin(); it != fReExportedChildren.end(); it++) { - ObjectFile::Reader* child = *it; - // check child is not already in chain - for (ReExportChain* p = prev; p != NULL; p = p->prev) { - if ( p->reader == child ) { - throwf("cycle in dylib re-exports with %s", child->getPath()); - } - } - ((Reader<A>*)(*it))->assertNoReExportCycles(&chain); - } -} - - -template <typename A> -std::vector<const char*>* Reader<A>::getAllowableClients() -{ - std::vector<const char*>* result = new std::vector<const char*>; - for (typename std::vector<const char*>::iterator it = fAllowableClients.begin(); - it != fAllowableClients.end(); - it++) { - result->push_back(*it); - } - return (fAllowableClients.size() != 0 ? result : NULL); -} - -template <> -bool Reader<ppc>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader<ppc64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_I386 ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_X86_64 ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -template <> -bool Reader<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_ARM ) - return false; - switch ( header->filetype() ) { - case MH_DYLIB: - case MH_DYLIB_STUB: - return true; - case MH_BUNDLE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; - case MH_EXECUTE: - if ( executableOrDyliborBundle ) - return true; - else - throw "can't link with a main executable"; - default: - return false; - } -} - -}; // namespace dylib -}; // namespace mach_o - - -#endif // __OBJECT_FILE_DYLIB_MACH_O__ diff --git a/src/ld/MachOReaderRelocatable.hpp b/src/ld/MachOReaderRelocatable.hpp deleted file mode 100644 index 2aa7926..0000000 --- a/src/ld/MachOReaderRelocatable.hpp +++ /dev/null @@ -1,6125 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OBJECT_FILE_MACH_O__ -#define __OBJECT_FILE_MACH_O__ - -#include <stdint.h> -#include <math.h> -#include <unistd.h> -#include <sys/param.h> - -#include <vector> -#include <set> -#include <algorithm> - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ObjectFile.h" -#include "dwarf2.h" -#include "debugline.h" - -#include <libunwind/DwarfInstructions.hpp> -#include <libunwind/AddressSpace.hpp> -#include <libunwind/Registers.hpp> - -// -// -// To implement architecture xxx, you must write template specializations for the following six methods: -// Reader<xxx>::validFile() -// Reader<xxx>::validSectionType() -// Reader<xxx>::addRelocReference() -// Reference<xxx>::getDescription() -// -// - - - -extern __attribute__((noreturn)) void throwf(const char* format, ...); -extern void warning(const char* format, ...); - -namespace mach_o { -namespace relocatable { - - - -class ReferenceSorter -{ -public: - bool operator()(const ObjectFile::Reference* left, const ObjectFile::Reference* right) - { - return ( left->getFixUpOffset() < right->getFixUpOffset() ); - } -}; - - -// forward reference -template <typename A> class Reader; - -struct AtomAndOffset -{ - AtomAndOffset(ObjectFile::Atom* a=NULL) : atom(a), offset(0) {} - AtomAndOffset(ObjectFile::Atom* a, uint32_t off) : atom(a), offset(off) {} - ObjectFile::Atom* atom; - uint32_t offset; -}; - - -template <typename A> -class Reference : public ObjectFile::Reference -{ -public: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - - Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget); - Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget); - Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset); - - virtual ~Reference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const; - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const; - virtual uint8_t getKind() const { return (uint8_t)fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getName() : fToTargetName; } - virtual ObjectFile::Atom& getTarget() const { return *fToTarget.atom; } - virtual uint64_t getTargetOffset() const { return (int64_t)((int32_t)fToTarget.offset); } - virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget.atom; } - virtual const char* getFromTargetName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getName() : fFromTargetName; } - virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fToTarget.atom = ⌖ fToTarget.offset = offset; } - virtual void setToTargetOffset(uint64_t offset) { fToTarget.offset = offset; } - virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget.atom = ⌖ } - virtual void setFromTargetName(const char* name) { fFromTargetName = name; } - virtual void setFromTargetOffset(uint64_t offset) { fFromTarget.offset = offset; } - virtual const char* getDescription() const; - virtual uint64_t getFromTargetOffset() const { return fFromTarget.offset; } - virtual bool isBranch() const; - virtual const char* getTargetDisplayName() const { return (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName; } - virtual const char* getFromTargetDisplayName() const { return (fFromTarget.atom != NULL) ? fFromTarget.atom->getDisplayName() : fFromTargetName; } - - static bool fgForFinalLinkedImage; - -private: - pint_t fFixUpOffsetInSrc; - AtomAndOffset fToTarget; - AtomAndOffset fFromTarget; - const char* fToTargetName; - const char* fFromTargetName; - Kinds fKind; - -}; - - -template <typename A> bool Reference<A>::fgForFinalLinkedImage = true; - -template <typename A> -Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& toTarget) - : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fToTargetName(NULL), fFromTargetName(NULL), - fKind(kind) -{ - // make reference a by-name unless: - // - the reference type is only used with direct references - // - the target is translation unit scoped - // - the target kind is not regular (is weak or tentative) - if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) - && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) - && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && (toTarget.atom != at.atom) ) { - fToTargetName = toTarget.atom->getName(); - //fprintf(stderr, "Reference(): changing to by-name %p %s, target scope=%d, target section=%s\n", toTarget.atom, fToTargetName, toTarget.atom->getScope(), toTarget.atom->getSectionName()); - fToTarget.atom = NULL; - } - ((class BaseAtom*)at.atom)->addReference(this); - //fprintf(stderr, "Reference(): %p fToTarget<%s, %08X>\n", this, (fToTarget.atom != NULL) ? fToTarget.atom->getDisplayName() : fToTargetName , fToTarget.offset); -} - -template <typename A> -Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const AtomAndOffset& fromTarget, const AtomAndOffset& toTarget) - : fFixUpOffsetInSrc(at.offset), fToTarget(toTarget), fFromTarget(fromTarget), - fToTargetName(NULL), fFromTargetName(NULL), fKind(kind) -{ - // make reference a by-name where needed - if ( (kind != A::kNoFixUp) && (kind != A::kFollowOn) && (kind != A::kGroupSubordinate) - && (toTarget.atom->getScope() != ObjectFile::Atom::scopeTranslationUnit) - && (toTarget.atom->getDefinitionKind() != ObjectFile::Atom::kRegularDefinition) - && (toTarget.atom != at.atom) ) { - fToTargetName = toTarget.atom->getName(); - fToTarget.atom = NULL; - } - ((class BaseAtom*)at.atom)->addReference(this); - //fprintf(stderr, "Reference(): %p kind=%d, fToTarget<%s, %08X>, fromTarget<%s, %08X>\n", this, kind, - // this->getTargetName(), fToTarget.offset, this->getFromTargetName(), fromTarget.offset); -} - -template <typename A> -Reference<A>::Reference(Kinds kind, const AtomAndOffset& at, const char* toName, uint32_t toOffset) - : fFixUpOffsetInSrc(at.offset), - fToTargetName(toName), fFromTargetName(NULL), fKind(kind) -{ - fToTarget.offset = toOffset; - ((class BaseAtom*)at.atom)->addReference(this); -} - -template <typename A> -ObjectFile::Reference::TargetBinding Reference<A>::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 <typename A> -ObjectFile::Reference::TargetBinding Reference<A>::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 <typename A> -class Segment : public ObjectFile::Segment -{ -public: - Segment(const macho_section<typename A::P>* sect); - virtual const char* getName() const { return fSection->segname(); } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } -private: - const macho_section<typename A::P>* fSection; - bool fWritable; - bool fExecutable; -}; - -template <typename A> -Segment<A>::Segment(const macho_section<typename A::P>* sect) - : fSection(sect), fWritable(true), fExecutable(false) -{ - if ( strcmp(fSection->segname(), "__TEXT") == 0 ) { - fWritable = false; - fExecutable = true; - } - else if ( strcmp(fSection->segname(), "__IMPORT") == 0 ) { - fWritable = true; - fExecutable = true; - } -} - - -class DataSegment : public ObjectFile::Segment -{ -public: - virtual const char* getName() const { return "__DATA"; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return true; } - virtual bool isContentExecutable() const { return false; } - - static DataSegment fgSingleton; -}; - -DataSegment DataSegment::fgSingleton; - -class LinkEditSegment : public ObjectFile::Segment -{ -public: - virtual const char* getName() const { return "__LINKEDIT"; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return false; } - - static LinkEditSegment fgSingleton; -}; - -LinkEditSegment LinkEditSegment::fgSingleton; - -class BaseAtom : public ObjectFile::Atom -{ -public: - BaseAtom() : fStabsStartIndex(0), fStabsCount(0), fHasCompactUnwindInfo(false) {} - - virtual void setSize(uint64_t size) = 0; - virtual void addReference(ObjectFile::Reference* ref) = 0; - virtual void sortReferences() = 0; - virtual void addLineInfo(const ObjectFile::LineInfo& info) = 0; - virtual const ObjectFile::ReaderOptions& getOptions() const = 0; - virtual uint64_t getObjectAddress() const = 0; - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual void setOrdinal(uint32_t value) { fOrdinal = value; } - virtual const void* getSectionRecord() const = 0; - virtual unsigned int getSectionIndex() const = 0; - virtual bool isAlias() const { return false; } - virtual uint8_t getLSDAReferenceKind() const { return 0; } - virtual uint8_t getPersonalityReferenceKind() const { return 0; } - virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress) { return 0; } - virtual ObjectFile::UnwindInfo::iterator beginUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[0] : NULL; } - virtual ObjectFile::UnwindInfo::iterator endUnwind() { return fHasCompactUnwindInfo ? &fSingleUnwindInfo[1] : NULL; } - virtual ObjectFile::Reference* getLSDA(); - virtual ObjectFile::Reference* getFDE(); - virtual Atom* getPersonalityPointer(); - virtual void setCompactUnwindEncoding(uint64_t ehAtomAddress); - - uint32_t fStabsStartIndex; - uint32_t fStabsCount; - uint32_t fOrdinal; - ObjectFile::UnwindInfo fSingleUnwindInfo[1]; - bool fHasCompactUnwindInfo; -}; - - -ObjectFile::Reference* BaseAtom::getLSDA() -{ - const uint8_t groupKind = this->getLSDAReferenceKind(); - const std::vector<ObjectFile::Reference*>& refs = this->getReferences(); - for (std::vector<ObjectFile::Reference*>::const_iterator it=refs.begin(); it != refs.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kLSDAType) ) { - return ref; - } - } - return NULL; -} - -ObjectFile::Reference* BaseAtom::getFDE() -{ - const uint8_t groupKind = this->getLSDAReferenceKind(); - const std::vector<ObjectFile::Reference*>& refs = this->getReferences(); - for (std::vector<ObjectFile::Reference*>::const_iterator it=refs.begin(); it != refs.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( (ref->getKind() == groupKind) && (ref->getTarget().getContentType() == ObjectFile::Atom::kCFIType) ) { - return ref; - } - } - return NULL; -} - -ObjectFile::Atom* BaseAtom::getPersonalityPointer() -{ - const uint8_t personalityKind = this->getPersonalityReferenceKind(); - const std::vector<ObjectFile::Reference*>& refs = this->getReferences(); - for (std::vector<ObjectFile::Reference*>::const_iterator it=refs.begin(); it != refs.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( ref->getKind() == personalityKind ) { - if ( strcmp(ref->getTarget().getSectionName(), "__nl_symbol_ptr") == 0 ) - return &ref->getTarget(); - if ( strcmp(ref->getTarget().getSectionName(), "__pointers") == 0 ) - return &ref->getTarget(); - } - } - return NULL; -} - - -void BaseAtom::setCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - fSingleUnwindInfo[0].unwindInfo = this->getCompactUnwindEncoding(ehAtomAddress); - fHasCompactUnwindInfo = true; -} - - -class BaseAtomSorter -{ -public: - bool operator()(const class BaseAtom* left, const class BaseAtom* right) { - if ( left == right ) - return false; - uint64_t leftAddr = left->getObjectAddress(); - uint64_t rightAddr = right->getObjectAddress(); - if ( leftAddr < rightAddr ) { - return true; - } - else if ( leftAddr > rightAddr ) { - return false; - } - else { - // if they have same address, one might be the end of a section and the other the start of the next section - const void* leftSection = left->getSectionRecord(); - const void* rightSection = right->getSectionRecord(); - if ( leftSection != rightSection ) { - return ( leftSection < rightSection ); - } - // if they have same address and section, one might be an alias - bool leftAlias = left->isAlias(); - bool rightAlias = right->isAlias(); - if ( leftAlias && rightAlias ) { - // sort multiple aliases for same address first by scope - ObjectFile::Atom::Scope leftScope = left->getScope(); - ObjectFile::Atom::Scope rightScope = right->getScope(); - if ( leftScope != rightScope ) { - return ( leftScope < rightScope ); - } - // sort multiple aliases for same address then by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - } - else if ( leftAlias ) { - return true; - } - else if ( rightAlias ) { - return false; - } - // one might be a section start or end label - switch ( left->getContentType() ) { - case ObjectFile::Atom::kSectionStart: - return true; - case ObjectFile::Atom::kSectionEnd: - return false; - default: - break; - } - switch ( right->getContentType() ) { - case ObjectFile::Atom::kSectionStart: - return false; - case ObjectFile::Atom::kSectionEnd: - return true; - default: - break; - } - // they could be tentative defintions - switch ( left->getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - // sort tentative definitions by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - case ObjectFile::Atom::kAbsoluteSymbol: - // sort absolute symbols with same address by name - return ( strcmp(left->getName(), right->getName()) < 0 ); - default: - // hack for rdar://problem/5102873 - if ( !left->isZeroFill() || !right->isZeroFill() ) - warning("atom sorting error for %s and %s in %s", left->getDisplayName(), right->getDisplayName(), left->getFile()->getPath()); - break; - } - } - return false; - } -}; - - -// -// A SymbolAtom represents a chunk of a mach-o object file that has a symbol table entry -// pointing to it. A C function or global variable is represented by one of these atoms. -// -// -template <typename A> -class SymbolAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ((fSymbol->n_desc() & N_WEAK_DEF) != 0) - ? ObjectFile::Atom::kWeakDefinition : ObjectFile::Atom::kRegularDefinition; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fType; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } - virtual bool dontDeadStrip() const; - virtual bool isZeroFill() const; - virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } - virtual uint64_t getSize() const { return fSize; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual Segment<A>& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const; - virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return (std::vector<ObjectFile::LineInfo>*)&fLineInfo; } - virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size); - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { fLineInfo.push_back(info); } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return fAddress; } - virtual const void* getSectionRecord() const { return (const void*)fSection; } - virtual unsigned int getSectionIndex() const { return 1 + (fSection - fOwner.fSectionsStart); } - virtual uint8_t getLSDAReferenceKind() const; - virtual uint8_t getPersonalityReferenceKind() const; - virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress); - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - typedef typename std::vector<Reference<A>*> 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<A>; - - SymbolAtom(Reader<A>&, const macho_nlist<P>*, const macho_section<P>*); - virtual ~SymbolAtom() {} - - Reader<A>& fOwner; - const macho_nlist<P>* fSymbol; - pint_t fAddress; - pint_t fSize; - const macho_section<P>* fSection; - Segment<A>* fSegment; - ReferenceVector fReferences; - std::vector<ObjectFile::LineInfo> fLineInfo; - ObjectFile::Atom::Scope fScope; - SymbolTableInclusion fSymbolTableInclusion; - ObjectFile::Atom::ContentType fType; - ObjectFile::Alignment fAlignment; -}; - - -template <typename A> -SymbolAtom<A>::SymbolAtom(Reader<A>& owner, const macho_nlist<P>* symbol, const macho_section<P>* section) - : fOwner(owner), fSymbol(symbol), fAddress(0), fSize(0), fSection(section), fSegment(NULL), fType(ObjectFile::Atom::kUnclassifiedType), fAlignment(0) -{ - fSingleUnwindInfo[0].startOffset = 0; - fSingleUnwindInfo[0].unwindInfo = 0; - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - if ( (type & N_TYPE) == N_SECT ) { - // real definition - fSegment = new Segment<A>(fSection); - fAddress = fSymbol->n_value(); - pint_t sectionStartAddr = section->addr(); - pint_t sectionEndAddr = section->addr()+section->size(); - if ( (fAddress < sectionStartAddr) || (fAddress > (sectionEndAddr)) ) { - throwf("malformed .o file, symbol %s with address 0x%0llX is not with section %d (%s,%s) address range of 0x%0llX to 0x%0llX", - this->getName(), (uint64_t)fAddress, fSymbol->n_sect(), section->segname(), section->sectname(), - (uint64_t)sectionStartAddr, (uint64_t)(sectionEndAddr) ); - } - } - else { - warning("unknown symbol type: %d", type); - } - - //fprintf(stderr, "SymbolAtom(%p) %s fAddress=0x%X\n", this, this->getDisplayName(), (uint32_t)fAddress); - // support for .o files built with old ld64 - if ( (fSymbol->n_desc() & N_WEAK_DEF) && (strcmp(fSection->sectname(),"__picsymbolstub1__TEXT") == 0) ) { - const char* name = this->getName(); - const int nameLen = strlen(name); - if ( (nameLen > 6) && strcmp(&name[nameLen-5], "$stub") == 0 ) { - // switch symbol to point at name that does not have trailing $stub - char correctName[nameLen]; - strncpy(correctName, name, nameLen-5); - correctName[nameLen-5] = '\0'; - const macho_nlist<P>* symbolsStart = fOwner.fSymbols; - const macho_nlist<P>* symbolsEnd = &symbolsStart[fOwner.fSymbolCount]; - for(const macho_nlist<P>* s = symbolsStart; s < symbolsEnd; ++s) { - if ( strcmp(&fOwner.fStrings[s->n_strx()], correctName) == 0 ) { - fSymbol = s; - break; - } - } - } - } - // support for labeled stubs - switch ( section->flags() & SECTION_TYPE ) { - case S_SYMBOL_STUBS: - setSize(section->reserved2()); - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - setSize(sizeof(pint_t)); - break; - case S_4BYTE_LITERALS: - setSize(4); - break; - case S_8BYTE_LITERALS: - setSize(8); - break; - case S_16BYTE_LITERALS: - setSize(16); - break; - case S_CSTRING_LITERALS: - setSize(strlen((char*)(fOwner.fHeader) + section->offset() + fAddress - section->addr()) + 1); - fType = ObjectFile::Atom::kCStringType; - break; - case S_REGULAR: - case S_ZEROFILL: - case S_COALESCED: - // size calculate later after next atom is found - break; - } - - // compute alignment - fAlignment = ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); - - // compute whether this atom needs to be in symbol table - if ( (fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) { - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAndNeverStrip; - } - else if ( fOwner.fOptions.fForFinalLinkedImage && !fOwner.fOptions.fForStatic && (fOwner.fStrings[fSymbol->n_strx()] == 'l') ) { - // labels beginning with a lowercase ell are automatically removed in final linked images <rdar://problem/4571042> - // xnu code base uses a lot of asesembly labels that start with 'l', don't strip those (static executable) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableNotIn; - } - else { - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - - // work around malformed icc generated .o files <rdar://problem/5349847> - // 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 <typename A> -bool SymbolAtom<A>::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 <typename A> -const char* SymbolAtom<A>::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage ) { - if ( strcmp(fSection->sectname(), "__textcoal_nt") == 0 ) - return "__text"; - else if ( strcmp(fSection->sectname(), "__const_coal") == 0 ) - return "__const"; - else if ( strcmp(fSection->sectname(), "__datacoal_nt") == 0 ) - return "__data"; - else if ( fOwner.fOptions.fAutoOrderInitializers && (strcmp(fSection->sectname(), "__StaticInit") == 0) ) - return "__text"; - else { - switch ( fSection->flags() & SECTION_TYPE ) { - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - return "__const"; - } - } - } - - if ( strlen(fSection->sectname()) > 15 ) { - static char temp[18]; - strncpy(temp, fSection->sectname(), 16); - temp[17] = '\0'; - return temp; - } - return fSection->sectname(); -} - -template <typename A> -ObjectFile::Atom& SymbolAtom<A>::getFollowOnAtom() const -{ - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference<A>* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return ref->getTarget(); - } - return *((ObjectFile::Atom*)NULL); -} - -template <typename A> -bool SymbolAtom<A>::isZeroFill() const -{ - return ( ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL) && fOwner.fOptions.fOptimizeZeroFill ); -} - - -class Beyond -{ -public: - Beyond(uint64_t offset) : fOffset(offset) {} - bool operator()(ObjectFile::Reference* ref) const { - return ( ref->getFixUpOffset() >= fOffset ); - } -private: - uint64_t fOffset; -}; - - -template <typename A> -void SymbolAtom<A>::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 <typename A> -void SymbolAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - // copy base bytes - if ( isZeroFill() ) - bzero(buffer, fSize); - else { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); - } -} - - - - -// -// A SymbolAliasAtom represents an alternate name for a SymbolAtom -// -// -template <typename A> -class SymbolAliasAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return fAliasOf.getFile(); } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fAliasOf.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fAliasOf.getDefinitionKind(); } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return fAliasOf.getSymbolTableInclusion(); } - virtual bool dontDeadStrip() const { return fDontDeadStrip; } - virtual bool isZeroFill() const { return fAliasOf.isZeroFill(); } - virtual bool isThumb() const { return fAliasOf.isThumb(); } - virtual uint64_t getSize() const { return 0; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const { return fAliasOf.getSectionName(); } - virtual Segment<A>& getSegment() const { return (Segment<A>&)fAliasOf.getSegment(); } - virtual ObjectFile::Atom& getFollowOnAtom() const { return (ObjectFile::Atom&)fAliasOf; } - virtual std::vector<ObjectFile::LineInfo>* 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<A>*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fAliasOf.getOptions(); } - virtual uint64_t getObjectAddress() const { return fAliasOf.getObjectAddress(); } - virtual const void* getSectionRecord() const { return fAliasOf.getSectionRecord(); } - virtual unsigned int getSectionIndex() const { return fAliasOf.getSectionIndex(); } - virtual bool isAlias() const { return true; } - -protected: - typedef typename A::P P; - typedef typename std::vector<Reference<A>*> 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<A>; - - SymbolAliasAtom(const char* name, const macho_nlist<P>*, const BaseAtom& ); - virtual ~SymbolAliasAtom() {} - - const char* fName; - const BaseAtom& fAliasOf; - ObjectFile::Atom::Scope fScope; - bool fDontDeadStrip; - ReferenceVector fReferences; -}; - - -template <typename A> -SymbolAliasAtom<A>::SymbolAliasAtom(const char* name, const macho_nlist<P>* 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>(A::kFollowOn, AtomAndOffset(this), AtomAndOffset((ObjectFile::Atom*)&aliasOf)); -} - - -// -// A TentativeAtom represents a C "common" or "tentative" defintion of data. -// For instance, "int foo;" is neither a declaration or a definition and -// is represented by a TentativeAtom. -// -template <typename A> -class TentativeAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kTentativeDefinition; } - virtual bool isZeroFill() const { return fOwner.fOptions.fOptimizeZeroFill; } - virtual bool isThumb() const { return false; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ((fSymbol->n_desc() & REFERENCED_DYNAMICALLY) != 0) - ? ObjectFile::Atom::kSymbolTableInAndNeverStrip : ObjectFile::Atom::kSymbolTableIn; } - virtual bool dontDeadStrip() const { return ((fSymbol->n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0); } - virtual uint64_t getSize() const { return fSymbol->n_value(); } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgNoReferences; } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual ObjectFile::Segment& getSegment() const { return DataSegment::fgSingleton; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } - virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return ULLONG_MAX; } - virtual const void* getSectionRecord() const { return NULL; } - virtual unsigned int getSectionIndex() const { return 0; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader<A>; - - TentativeAtom(Reader<A>&, const macho_nlist<P>*); - virtual ~TentativeAtom() {} - - Reader<A>& fOwner; - const macho_nlist<P>* fSymbol; - ObjectFile::Atom::Scope fScope; - static std::vector<ObjectFile::Reference*> fgNoReferences; -}; - -template <typename A> -std::vector<ObjectFile::Reference*> TentativeAtom<A>::fgNoReferences; - -template <typename A> -TentativeAtom<A>::TentativeAtom(Reader<A>& owner, const macho_nlist<P>* symbol) - : fOwner(owner), fSymbol(symbol) -{ - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - if ( ((type & N_TYPE) == N_UNDF) && (symbol->n_value() != 0) ) { - // tentative definition - } - else { - warning("unknown symbol type: %d", type); - } - //fprintf(stderr, "TentativeAtom(%p) %s\n", this, this->getDisplayName()); -} - - -template <typename A> -ObjectFile::Alignment TentativeAtom<A>::getAlignment() const -{ - uint8_t alignment = GET_COMM_ALIGN(fSymbol->n_desc()); - if ( alignment == 0 ) { - // common symbols align to their size - // that is, a 4-byte common aligns to 4-bytes - // if this size is not a power of two, - // then round up to the next power of two - uint64_t size = this->getSize(); - alignment = 63 - (uint8_t)__builtin_clzll(size); - if ( size != (1ULL << alignment) ) - ++alignment; - } - // limit alignment of extremely large commons to 2^15 bytes (8-page) - if ( alignment < 12 ) - return ObjectFile::Alignment(alignment); - else - return ObjectFile::Alignment(12); -} - -template <typename A> -const char* TentativeAtom<A>::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage || fOwner.fOptions.fMakeTentativeDefinitionsReal ) - return "__common"; - else - return "._tentdef"; -} - - -template <typename A> -void TentativeAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - -// -// An AnonymousAtom represents compiler generated data that has no name. -// For instance, a literal C-string or a 64-bit floating point constant -// is represented by an AnonymousAtom. -// -template <typename A> -class AnonymousAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fSynthesizedName; } - virtual const char* getDisplayName() const; - virtual ObjectFile::Atom::Scope getScope() const; - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return fKind; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fType; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return fSymbolTableInclusion; } - virtual bool dontDeadStrip() const { return fDontDeadStrip; } - virtual bool isZeroFill() const; - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return fSize; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const; - virtual Segment<A>& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const; - virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { fSize = size; } - virtual void addReference(ObjectFile::Reference* ref) { fReferences.push_back((Reference<A>*)ref); } - virtual void sortReferences() { std::sort(fReferences.begin(), fReferences.end(), ReferenceSorter()); } - virtual void addLineInfo(const ObjectFile::LineInfo& info); - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return fAddress; } - virtual const void* getSectionRecord() const { return (const void*)fSection; } - virtual unsigned int getSectionIndex() const { return fSectionIndex; } - BaseAtom* redirectTo() { return fRedirect; } - bool isWeakImportStub() { return fWeakImportStub; } - void resolveName(); - virtual uint8_t getLSDAReferenceKind() const; - virtual uint8_t getPersonalityReferenceKind() const; - virtual uint32_t getCompactUnwindEncoding(uint64_t ehAtomAddress); - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - typedef typename std::vector<Reference<A>*> 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<A>; - - AnonymousAtom(Reader<A>&, const macho_section<P>*, pint_t addr, pint_t size); - virtual ~AnonymousAtom() {} - static bool cstringsHaveLabels(); - - Reader<A>& fOwner; - const char* fSynthesizedName; - const char* fDisplayName; - const macho_section<P>* fSection; - pint_t fAddress; - pint_t fSize; - Segment<A>* fSegment; - ReferenceVector fReferences; - BaseAtom* fRedirect; - bool fDontDeadStrip; - bool fWeakImportStub; - ObjectFile::Atom::SymbolTableInclusion fSymbolTableInclusion; - ObjectFile::Atom::Scope fScope; - ObjectFile::Atom::DefinitionKind fKind; - ObjectFile::Atom::ContentType fType; - unsigned int fSectionIndex; -}; - -template <typename A> -AnonymousAtom<A>::AnonymousAtom(Reader<A>& owner, const macho_section<P>* section, pint_t addr, pint_t size) - : fOwner(owner), fSynthesizedName(NULL), fDisplayName(NULL), fSection(section), fAddress(addr), fSize(size), - fSegment(NULL), fDontDeadStrip(true), fWeakImportStub(false), fSymbolTableInclusion(ObjectFile::Atom::kSymbolTableNotIn), - fScope(ObjectFile::Atom::scopeTranslationUnit), fKind(ObjectFile::Atom::kRegularDefinition), - fType(ObjectFile::Atom::kUnclassifiedType), fSectionIndex(1 + (section - owner.fSectionsStart)) -{ - fSegment = new Segment<A>(fSection); - fRedirect = this; - uint8_t type = fSection->flags() & SECTION_TYPE; - //fprintf(stderr, "AnonymousAtom(%p) addr=0x%llX in %s from %s\n", this, (long long)addr, section->sectname(), owner.getPath()); - switch ( type ) { - case S_ZEROFILL: - { - asprintf((char**)&fSynthesizedName, "zero-fill-at-0x%08X", addr); - } - break; - case S_COALESCED: - case S_REGULAR: - if ( section == owner.fehFrameSection ) { - if ( fSize == 1 ) { - // is CIE - fSize = 0; - fDontDeadStrip = false; - if ( fOwner.fOptions.fForFinalLinkedImage ) - fSynthesizedName = "CIE"; - else - fSynthesizedName = "EH_frame1"; - } - else { - // is FDE - fSynthesizedName = ".eh_PENDING"; - fDontDeadStrip = false; - owner.fAtomsPendingAName.push_back(this); - } - fType = ObjectFile::Atom::kCFIType; - // FDEs and CIEs don't need to be in symbol table of final linked images <rdar://problem/4180168> - if ( !fOwner.fOptions.fNoEHLabels ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - else if ( (strcmp(section->sectname(), "__class") == 0) && (strcmp(section->segname(), "__OBJC") == 0) && owner.fAppleObjc ) { - // special case ObjC classes to synthesize .objc_class_name_* symbols, for Apple runtime only - fSynthesizedName = ".objc_class_name_PENDING"; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - if ( fOwner.fOptions.fForFinalLinkedImage ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - else - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableInAsAbsolute; - fScope = ObjectFile::Atom::scopeGlobal; - } - else if ( strcmp(fSection->sectname(), "__cstring") == 0 ) { - // handle .o files created by old ld64 -r that are missing cstring section type - const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - asprintf((char**)&fSynthesizedName, "cstring=%s", str); - } - else if ((strcmp(section->sectname(), "__cfstring") == 0) && (strcmp(section->segname(), "__DATA") == 0)) { - fSynthesizedName = "cfstring-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - fDontDeadStrip = false; - fKind = ObjectFile::Atom::kWeakDefinition; - } - else if ( (fSection->flags() & S_ATTR_SOME_INSTRUCTIONS) != 0 ) { - fDontDeadStrip = false; - asprintf((char**)&fSynthesizedName, "anon-func-0x%X", addr); - } - else if ( strncmp(fSection->sectname(), "__gcc_except_tab",16) == 0 ) { - fType = ObjectFile::Atom::kLSDAType; - fDontDeadStrip = false; - fSynthesizedName = ".lsda_PENDING"; - owner.fAtomsPendingAName.push_back(this); - if ( !fOwner.fOptions.fNoEHLabels ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { - fSynthesizedName = "objc-class-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - fKind = ObjectFile::Atom::kWeakDefinition; - } - else if ( section == owner.fUTF16Section ) { - if ( fOwner.fOptions.fForFinalLinkedImage ) { - fDontDeadStrip = false; - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - char* name = new char[16+5*size]; - strcpy(name, "utf16-string="); - char* s = &name[13]; - const uint16_t* words = (uint16_t*)((char*)(owner.fHeader) + section->offset() + addr - section->addr()); - unsigned int wordCount = size/2; - bool needSeperator = false; - for(unsigned int i=0; i < wordCount; ++i) { - if ( needSeperator ) - strcpy(s++, "."); - sprintf(s, "%04X", E::get32(words[i])); - s += 4; - needSeperator = true; - } - fSynthesizedName = name; - } - else { - asprintf((char**)&fSynthesizedName, "lutf16-0x%X", addr); - } - } - break; - case S_CSTRING_LITERALS: - { - const char* str = (char*)(owner.fHeader) + section->offset() + addr - section->addr(); - if ( (strcmp(fSection->sectname(), "__cstring") == 0) && (strcmp(section->segname(), "__TEXT") == 0) ) - asprintf((char**)&fSynthesizedName, "cstring=%s", str); - else - asprintf((char**)&fSynthesizedName, "cstring%s%s=%s", fSection->segname(), fSection->sectname(), str); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fType = ObjectFile::Atom::kCStringType; - fDontDeadStrip = false; - if ( !fOwner.fOptions.fForFinalLinkedImage && cstringsHaveLabels() ) - fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; - } - break; - case S_4BYTE_LITERALS: - { - uint32_t value = E::get32(*(uint32_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - asprintf((char**)&fSynthesizedName, "4-byte-literal=0x%08X", value); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_8BYTE_LITERALS: - { - uint64_t value = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - asprintf((char**)&fSynthesizedName, "8-byte-literal=0x%016llX", value); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_16BYTE_LITERALS: - { - uint64_t value1 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - uint64_t value2 = E::get64(*(uint64_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr + 8 - section->addr())); - asprintf((char**)&fSynthesizedName, "16-byte-literal=0x%016llX,%016llX", value1, value2); - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - } - break; - case S_LITERAL_POINTERS: - { - //uint32_t literalNameAddr = P::getP(*(pint_t*)(((uint8_t*)owner.fHeader) + section->offset() + addr - section->addr())); - //const char* str = (char*)(owner.fHeader) + section->offset() + literalNameAddr - section->addr(); - //asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", section->segname(), section->sectname(), str); - fSynthesizedName = "literal-pointer-name-PENDING"; - fScope = ObjectFile::Atom::scopeLinkageUnit; - fKind = ObjectFile::Atom::kWeakDefinition; - fDontDeadStrip = false; - owner.fAtomsPendingAName.push_back(this); - owner.fSectionsWithAtomsPendingAName.insert(fSection); - } - break; - case S_MOD_INIT_FUNC_POINTERS: - asprintf((char**)&fSynthesizedName, "initializer$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); - break; - case S_MOD_TERM_FUNC_POINTERS: - asprintf((char**)&fSynthesizedName, "terminator$%d", (addr - (uint32_t)fSection->addr())/sizeof(pint_t)); - break; - case S_SYMBOL_STUBS: - { - uint32_t index = (fAddress - fSection->addr()) / fSection->reserved2(); - index += fSection->reserved1(); - uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); - const macho_nlist<P>* sym = &fOwner.fSymbols[symbolIndex]; - uint32_t strOffset = sym->n_strx(); - // want name to not have $stub suffix, this is what automatic stub generation expects - fSynthesizedName = &fOwner.fStrings[strOffset]; - // check for weak import - fWeakImportStub = fOwner.isWeakImportSymbol(sym); - // sometimes the compiler gets confused and generates a stub to a static function - // if so, we should redirect any call to the stub to be calls to the real static function atom - if ( ((sym->n_type() & N_TYPE) != N_UNDF) && ((sym->n_type() & N_EXT) == 0) ) { - BaseAtom* staticAtom = fOwner.findAtomByName(fSynthesizedName); - if ( staticAtom != NULL ) - fRedirect = staticAtom; - } - fKind = ObjectFile::Atom::kWeakDefinition; - // might be a spurious stub for a static function, make stub static too - if ( (sym->n_type() & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else - fScope = ObjectFile::Atom::scopeLinkageUnit; - } - break; - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - { - // transform i386 __IMPORT/__pointers to __DATA/__nl_symbol_ptr when - // generating the new compressed LINKEDIT format - if ( (type == S_NON_LAZY_SYMBOL_POINTERS) && fOwner.fOptions.fMakeCompressedDyldInfo && (strcmp(fSection->segname(),"__IMPORT") == 0) ) { - macho_section<P>* dummySection = new macho_section<P>(*fSection); - dummySection->set_segname("__DATA"); - dummySection->set_sectname("__nl_symbol_ptr"); - fSection = dummySection; - fSegment = new Segment<A>(fSection); - } - - fDontDeadStrip = false; - fScope = ObjectFile::Atom::scopeLinkageUnit; - uint32_t index = (fAddress - fSection->addr()) / sizeof(pint_t); - index += fSection->reserved1(); - uint32_t symbolIndex = E::get32(fOwner.fIndirectTable[index]); - if ( symbolIndex == INDIRECT_SYMBOL_LOCAL ) { - // Silly codegen with non-lazy pointer to a local symbol - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fOwner.fHeader)+fileOffset))); - // All atoms not created yet, so we need to scan symbol table - const macho_nlist<P>* closestSym = NULL; - const macho_nlist<P>* end = &fOwner.fSymbols[fOwner.fSymbolCount]; - for (const macho_nlist<P>* sym = fOwner.fSymbols; sym < end; ++sym) { - if ( ((sym->n_type() & N_TYPE) == N_SECT) - && ((sym->n_type() & N_STAB) == 0) ) { - if ( sym->n_value() == nonLazyPtrValue ) { - const char* name = &fOwner.fStrings[sym->n_strx()]; - char* str = new char[strlen(name)+16]; - strcpy(str, name); - strcat(str, "$non_lazy_ptr"); - fSynthesizedName = str; - // add direct reference to target later, because its atom may not be constructed yet - fOwner.fLocalNonLazys.push_back(this); - fScope = ObjectFile::Atom::scopeTranslationUnit; - fType = ObjectFile::Atom::kNonLazyPointer; - return; - } - else if ( (sym->n_value() < nonLazyPtrValue) && ((closestSym == NULL) || (sym->n_value() > closestSym->n_value())) ) { - closestSym = sym; - } - } - } - // add direct reference to target later, because its atom may not be constructed yet - if ( closestSym != NULL ) { - const char* name = &fOwner.fStrings[closestSym->n_strx()]; - char* str; - asprintf(&str, "%s+%u$non_lazy_ptr", name, nonLazyPtrValue - closestSym->n_value()); - fSynthesizedName = str; - } - else { - fSynthesizedName = "$interior$non_lazy_ptr"; - } - fScope = ObjectFile::Atom::scopeTranslationUnit; - fOwner.fLocalNonLazys.push_back(this); - fType = ObjectFile::Atom::kNonLazyPointer; - return; - } - const macho_nlist<P>* targetSymbol = &fOwner.fSymbols[symbolIndex]; - const char* name = &fOwner.fStrings[targetSymbol->n_strx()]; - char* str = new char[strlen(name)+16]; - strcpy(str, name); - if ( type == S_LAZY_SYMBOL_POINTERS ) { - strcat(str, "$lazy_ptr"); - fType = ObjectFile::Atom::kLazyPointer; - } - else { - strcat(str, "$non_lazy_ptr"); - fType = ObjectFile::Atom::kNonLazyPointer; - } - fSynthesizedName = str; - - if ( type == S_NON_LAZY_SYMBOL_POINTERS ) - fKind = ObjectFile::Atom::kWeakDefinition; - - if ( (targetSymbol->n_type() & N_EXT) == 0 ) { - // target is translation unit scoped, so add direct reference to target - //fOwner.makeReference(A::kPointer, addr, targetSymbol->n_value()); - new Reference<A>(A::kPointer, AtomAndOffset(this), fOwner.findAtomAndOffset(targetSymbol->n_value())); - } - else { - if ( fOwner.isWeakImportSymbol(targetSymbol) ) - new Reference<A>(A::kPointerWeakImport, AtomAndOffset(this), name, 0); - else - new Reference<A>(A::kPointer, AtomAndOffset(this), name, 0); - } - } - break; - default: - throwf("section type %d not supported with address=0x%08llX", type, (uint64_t)addr); - } - //fprintf(stderr, "AnonymousAtom(%p) %s \n", this, this->getDisplayName()); -} - -// x86_64 uses L labels on cstrings to allow relocs with addends -template <> bool AnonymousAtom<x86_64>::cstringsHaveLabels() { return true; } -template <typename A> bool AnonymousAtom<A>::cstringsHaveLabels() { return false; } - -template <typename A> -void AnonymousAtom<A>::addLineInfo(const ObjectFile::LineInfo& info) -{ - // <rdar://problem/6545406> don't warn if line table has entries for stubs - if ( (fSection->flags() & SECTION_TYPE) != S_SYMBOL_STUBS ) - warning("can't add line info to anonymous symbol %s from %s", this->getDisplayName(), this->getFile()->getPath()); -} - -template <typename A> -void AnonymousAtom<A>::resolveName() -{ - if ( (strcmp(fSection->sectname(), "__class") == 0) && (strcmp(fSection->segname(), "__OBJC") == 0) ) { - std::vector<ObjectFile::Reference*>& references = this->getReferences(); - // references are not yet sorted, so scan the vector - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* superStr = (*rit)->getTargetName(); - if ( strncmp(superStr, "cstring", 7) == 0 ) { - const char* superClassName; - asprintf((char**)&superClassName, ".objc_class_name_%s", &superStr[8]); - new Reference<A>(A::kNoFixUp, AtomAndOffset(this), superClassName, 0); - } - break; - } - } - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* classStr = (*rit)->getTargetName(); - if ( strncmp(classStr, "cstring", 7) == 0 ) { - asprintf((char**)&fSynthesizedName, ".objc_class_name_%s", &classStr[8]); - } - break; - } - } - } - else if ( (fSection->flags() & SECTION_TYPE) == S_LITERAL_POINTERS) { - std::vector<ObjectFile::Reference*>& references = this->getReferences(); - if ( references.size() < 1 ) - throwf("S_LITERAL_POINTERS section %s,%s missing relocs", fSection->segname(), fSection->sectname()); - ObjectFile::Reference* ref = references[0]; - const char* str = ref->getTargetName(); - if ( strncmp(str, "cstring", 7) == 0 ) { - asprintf((char**)&fSynthesizedName, "literal-pointer@%s@%s@%s", fSection->segname(), fSection->sectname(), &str[8]); - } - } - else if ( (strcmp(fSection->sectname(), "__cfstring") == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { - // references are not yet sorted, so scan the vector - std::vector<ObjectFile::Reference*>& references = this->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - if ( ((*rit)->getFixUpOffset() == 2*sizeof(pint_t)) && ((*rit)->getKind() == A::kPointer) ) { - const char* superStr = (*rit)->getTargetName(); - if ( (superStr != NULL) && (strncmp(superStr, "cstring=", 8) == 0) ) { - asprintf((char**)&fSynthesizedName, "cfstring=%s", &superStr[8]); - } - else if ( (superStr != NULL) && (strncmp(superStr, "utf16-string=", 13) == 0) ) { - asprintf((char**)&fSynthesizedName, "cfstring-utf16=%s", &superStr[13]); - } - else { - // compiled with -fwritable-strings or a non-ASCII string - fKind = ObjectFile::Atom::kRegularDefinition; // these are not coalescable - fScope = ObjectFile::Atom::scopeTranslationUnit; - fSynthesizedName = "cfstring-not-coalesable"; - if ( (*rit)->getTargetOffset() != 0 ) - warning("-fwritable-strings not compatible with literal CF/NSString in %s", fOwner.getPath()); - } - break; - } - } - } - else if ( fSection == fOwner.fehFrameSection ) { - // give name to FDE - ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromFDEAddress(fAddress); - if ( funcAtom != NULL ) - asprintf((char**)&fSynthesizedName, "%s.eh", funcAtom->getDisplayName()); - } - else if ( fOwner.fLSDAAtoms.count(this) != 0) { - // give name to LSDA - ObjectFile::Atom* funcAtom = fOwner.getFunctionAtomFromLSDAAddress(fAddress); - if ( funcAtom != NULL ) - asprintf((char**)&fSynthesizedName, "%s.lsda", funcAtom->getDisplayName()); - } - else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { - std::vector<ObjectFile::Reference*>& references = this->getReferences(); - if ( references.size() != 1 ) - throwf("__objc_classrefs element missing reloc (count=%ld) for target class in %s", references.size(), fOwner.getPath()); - const char* targetName = references[0]->getTargetName(); - if ( strncmp(targetName, "_OBJC_CLASS_$_", 14) == 0 ) - asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", &targetName[14]); - else - asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", targetName); - } -} - - -template <typename A> -const char* AnonymousAtom<A>::getDisplayName() const -{ - if ( fSynthesizedName != NULL ) - return fSynthesizedName; - - if ( fDisplayName != NULL ) - return fDisplayName; - - if ( (fSection->flags() & SECTION_TYPE) == S_CSTRING_LITERALS ) { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - asprintf((char**)&fDisplayName, "atom string literal: \"%s\"", (char*)(fOwner.fHeader)+fileOffset); - } - else { - asprintf((char**)&fDisplayName, "%s@%d", fSection->sectname(), fAddress - (uint32_t)fSection->addr() ); - } - return fDisplayName; -} - - -template <typename A> -ObjectFile::Atom::Scope AnonymousAtom<A>::getScope() const -{ - return fScope; -} - - -template <typename A> -bool AnonymousAtom<A>::isZeroFill() const -{ - return ( ((fSection->flags() & SECTION_TYPE) == S_ZEROFILL) && fOwner.fOptions.fOptimizeZeroFill ); -} - - -template <typename A> -const char* AnonymousAtom<A>::getSectionName() const -{ - if ( fOwner.fOptions.fForFinalLinkedImage ) { - switch ( fSection->flags() & SECTION_TYPE ) { - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - return "__const"; - } - } - - if ( strlen(fSection->sectname()) > 15 ) { - static char temp[18]; - strncpy(temp, fSection->sectname(), 16); - temp[17] = '\0'; - return temp; - } - return fSection->sectname(); -} - -template <typename A> -ObjectFile::Alignment AnonymousAtom<A>::getAlignment() const -{ - // FDEs and CIEs are always packed together in a final linked image, so ignore section alignment - if ( fType == ObjectFile::Atom::kCFIType ) - return ObjectFile::Alignment(0); - - switch ( fSection->flags() & SECTION_TYPE ) { - case S_4BYTE_LITERALS: - return ObjectFile::Alignment(2); - case S_8BYTE_LITERALS: - return ObjectFile::Alignment(3); - case S_16BYTE_LITERALS: - return ObjectFile::Alignment(4); - case S_NON_LAZY_SYMBOL_POINTERS: - return ObjectFile::Alignment((uint8_t)log2(sizeof(pint_t))); - case S_CSTRING_LITERALS: - if ( ! fOwner.fOptions.fForFinalLinkedImage ) - return ObjectFile::Alignment(fSection->align()); - default: - return ObjectFile::Alignment(fSection->align(), fAddress % (1 << fSection->align())); - } -} - - -template <typename A> -ObjectFile::Atom& AnonymousAtom<A>::getFollowOnAtom() const -{ - for (ReferenceVectorConstIterator it=fReferences.begin(); it != fReferences.end(); it++) { - Reference<A>* ref = *it; - if ( ref->getKind() == A::kFollowOn ) - return ref->getTarget(); - } - return *((ObjectFile::Atom*)NULL); -} - -template <typename A> -void AnonymousAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - // copy base bytes - if ( isZeroFill() ) - bzero(buffer, fSize); - else { - uint32_t fileOffset = fSection->offset() - fSection->addr() + fAddress; - memcpy(buffer, (char*)(fOwner.fHeader)+fileOffset, fSize); - } -} - -// -// An AbsoluteAtom represents an N_ABS symbol which can only be created in -// assembly language and usable by static executables such as the kernel/ -// -template <typename A> -class AbsoluteAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return &fOwner.fStrings[fSymbol->n_strx()]; } - virtual const char* getDisplayName() const { return getName(); } - virtual ObjectFile::Atom::Scope getScope() const { return fScope; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kAbsoluteSymbol; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return ((fSymbol->n_desc() & N_ARM_THUMB_DEF) != 0); } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableInAsAbsolute; } - virtual bool dontDeadStrip() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector<ObjectFile::Reference*>& 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<ObjectFile::LineInfo>* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(ObjectFile::Atom::Scope newScope) { fScope = newScope; } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return fSymbol->n_value(); } - virtual void setSectionOffset(uint64_t offset) { /* don't let fSectionOffset be altered*/ } - virtual const void* getSectionRecord() const { return NULL; } - virtual unsigned int getSectionIndex() const { return 0; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader<A>; - - AbsoluteAtom(Reader<A>&, const macho_nlist<P>*); - virtual ~AbsoluteAtom() {} - - Reader<A>& fOwner; - const macho_nlist<P>* fSymbol; - ObjectFile::Atom::Scope fScope; - static std::vector<ObjectFile::Reference*> fgNoReferences; -}; - -template <typename A> -std::vector<ObjectFile::Reference*> AbsoluteAtom<A>::fgNoReferences; - -template <typename A> -AbsoluteAtom<A>::AbsoluteAtom(Reader<A>& owner, const macho_nlist<P>* symbol) - : fOwner(owner), fSymbol(symbol) -{ - // store absolute adress in fSectionOffset - fSectionOffset = symbol->n_value(); - // compute scope - uint8_t type = symbol->n_type(); - if ( (type & N_EXT) == 0 ) - fScope = ObjectFile::Atom::scopeTranslationUnit; - else if ( (type & N_PEXT) != 0 ) - fScope = ObjectFile::Atom::scopeLinkageUnit; - else - fScope = ObjectFile::Atom::scopeGlobal; - //fprintf(stderr, "AbsoluteAtom(%p) %s\n", this, this->getDisplayName()); -} - - -// -// An SectionBoundaryAtom represent the start or end of a section -// -template <typename A> -class SectionBoundaryAtom : public BaseAtom -{ -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const - { return fOwner.getTranslationUnitSource(dir, name); } - virtual const char* getName() const { return fSymbolName; } - virtual const char* getDisplayName() const { return fDisplayName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kWeakDefinition; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fStart ? ObjectFile::Atom::kSectionStart : ObjectFile::Atom::kSectionEnd; } - virtual bool isZeroFill() const { return fZeroFill; } - virtual bool isThumb() const { return false; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return false; } - virtual uint64_t getSize() const { return 0; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgNoReferences; } - virtual bool mustRemainInSection() const { return true; } - virtual const char* getSectionName() const { return fSectionName; } - virtual ObjectFile::Segment& getSegment() const { return *fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *(ObjectFile::Atom*)NULL; } - virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(ObjectFile::Atom::Scope newScope) { } - virtual void setSize(uint64_t size) { } - virtual void addReference(ObjectFile::Reference* ref) { throw "ld: can't add references"; } - virtual void sortReferences() { } - virtual void addLineInfo(const ObjectFile::LineInfo& info) { throw "ld: can't add line info to tentative definition"; } - virtual const ObjectFile::ReaderOptions& getOptions() const { return fOwner.fOptions; } - virtual uint64_t getObjectAddress() const { return 0; } - virtual const void* getSectionRecord() const { return NULL; } - virtual unsigned int getSectionIndex() const { return 0; } - -protected: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - typedef typename A::ReferenceKinds Kinds; - friend class Reader<A>; - - - class Segment : public ObjectFile::Segment - { - public: - Segment(const char* name, bool r, bool w, bool x): - fName(name), fReadable(r), fWritable(w), fExecutable(x) {} - - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - private: - const char* fName; - bool fReadable; - bool fWritable; - bool fExecutable; - }; - - SectionBoundaryAtom(Reader<A>&, bool start, const char* symbolName, const char* segSectName); - virtual ~SectionBoundaryAtom() {} - - Reader<A>& fOwner; - class Segment* fSegment; - const char* fSymbolName; - const char* fSectionName; - const char* fDisplayName; - bool fStart; - bool fZeroFill; - static std::vector<ObjectFile::Reference*> fgNoReferences; -}; - -template <typename A> -std::vector<ObjectFile::Reference*> SectionBoundaryAtom<A>::fgNoReferences; - -// examples: -// section$start$__DATA$__my -// section$end$__DATA$__my -template <typename A> -SectionBoundaryAtom<A>::SectionBoundaryAtom(Reader<A>& owner, bool start, const char* symbolName, const char* segSectName) - : fOwner(owner), fSymbolName(symbolName), fSectionName(NULL), fStart(start), fZeroFill(false) -{ - const char* segSectDividor = strrchr(segSectName, '$'); - if ( segSectDividor == NULL ) - throwf("malformed section reference name: %s", symbolName); - fSectionName = segSectDividor + 1; - int segNameLen = segSectDividor - segSectName; - if ( segNameLen > 16 ) - throwf("malformed section reference name: %s", symbolName); - char segName[18]; - strlcpy(segName, segSectName, segNameLen+1); - if ( strcmp(segName, "__TEXT") == 0 ) - fSegment = new Segment("__TEXT", true, false, true); - else if ( strcmp(segName, "__DATA") == 0 ) { - fSegment = new Segment("__DATA", true, true, false); - if ( (strcmp(fSectionName, "__bss") == 0) || (strcmp(fSectionName, "__common") == 0) ) - fZeroFill = true; - } - else - fSegment = new Segment(strdup(segName), true, true, false); - - asprintf((char**)&fDisplayName, "%s of section '%s' in segment '%s'", (start ? "start" : "end"), fSectionName, segName); -} - - - -/// -/// ObjectFileAddressSpace is used as a template parameter to UnwindCursor for parsing -/// dwarf CFI information in an object file. -/// -template <typename A> -class ObjectFileAddressSpace -{ -public: - ObjectFileAddressSpace(Reader<A>& reader); - - typedef typename A::P::uint_t pint_t; - typedef typename A::P P; - typedef typename A::P::uint_t sint_t; - - uint8_t get8(pint_t addr); - uint16_t get16(pint_t addr); - uint32_t get32(pint_t addr); - uint64_t get64(pint_t addr); - pint_t getP(pint_t addr); - uint64_t getULEB128(pint_t& addr, pint_t end); - int64_t getSLEB128(pint_t& addr, pint_t end); - pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); -private: - const void* mappedAddress(pint_t addr, pint_t* relocTarget=NULL); - pint_t relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount); - void buildRelocatedMap(const macho_section<P>* sect, std::map<uint32_t,uint64_t>& map); - - Reader<A>& fReader; - const uint8_t* fMappingStart; - const macho_section<P>* fSectionsStart; - const macho_section<P>* fSectionsEnd; - std::map<uint32_t,uint64_t> fEHFrameOffsetToTargetMap; -}; - - -template <typename A> -ObjectFileAddressSpace<A>::ObjectFileAddressSpace(Reader<A>& reader) - : fReader(reader), fMappingStart(NULL), fSectionsStart(NULL), fSectionsEnd(NULL) -{ -} - - - -template <typename A> -const void* ObjectFileAddressSpace<A>::mappedAddress(pint_t addr, pint_t* relocTarget) -{ - if ( fMappingStart == NULL ) { - // delay initialization until now when fReader.fSegment is set up - fMappingStart = (uint8_t*)fReader.fHeader; - fSectionsStart = (macho_section<P>*)((char*)fReader.fSegment + sizeof(macho_segment_command<P>)); - fSectionsEnd = &fSectionsStart[fReader.fSegment->nsects()]; - // find __eh_frame section and build map of relocations for performance - buildRelocatedMap(fReader.fehFrameSection, fEHFrameOffsetToTargetMap); - } - // special case lookups in __eh_frame section to be fast - const macho_section<P>* ehSect = fReader.fehFrameSection; - if ( (ehSect->addr() <= addr) && (addr < (ehSect->addr()+ehSect->size())) ) { - pint_t offsetOfAddrInSection = addr - ehSect->addr(); - if ( relocTarget != NULL ) { - std::map<uint32_t,uint64_t>::iterator pos = fEHFrameOffsetToTargetMap.find(offsetOfAddrInSection); - if ( pos != fEHFrameOffsetToTargetMap.end() ) - *relocTarget = pos->second; - else - *relocTarget = 0; - } - return fMappingStart + ehSect->offset() + offsetOfAddrInSection; - } - else { - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { - pint_t offsetOfAddrInSection = addr - sect->addr(); - if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { - const uint32_t indirectTableOffset = sect->reserved1(); - const uint32_t sectionIndex = offsetOfAddrInSection/sizeof(pint_t); - const uint32_t symbolIndex = A::P::E::get32(fReader.fIndirectTable[indirectTableOffset+sectionIndex]); - // return pointer to symbol name which this non-lazy-pointer will point to - if ( relocTarget != NULL ) - *relocTarget = (uintptr_t)&fReader.fStrings[fReader.fSymbols[symbolIndex].n_strx()]; - } - else { - if ( relocTarget != NULL ) - *relocTarget = relocated(offsetOfAddrInSection, sect->reloff(), sect->nreloc()); - } - return fMappingStart + sect->offset() + offsetOfAddrInSection; - } - } - throwf("ObjectFileAddressSpace::mappedAddress(0x%0lX) not in any section", (long)addr); - } -} - - - - -template <typename A> -uint8_t ObjectFileAddressSpace<A>::get8(pint_t logicalAddr) -{ - return *((uint8_t*)mappedAddress(logicalAddr)); -} - -template <typename A> -uint16_t ObjectFileAddressSpace<A>::get16(pint_t logicalAddr) -{ - return P::E::get16(*((uint16_t*)mappedAddress(logicalAddr))); -} - -template <typename A> -uint32_t ObjectFileAddressSpace<A>::get32(pint_t logicalAddr) -{ - pint_t relocTarget; - return P::E::get32(*((uint32_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; -} - -template <typename A> -uint64_t ObjectFileAddressSpace<A>::get64(pint_t logicalAddr) -{ - pint_t relocTarget; - return P::E::get64(*((uint64_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; -} - -template <typename A> -typename A::P::uint_t ObjectFileAddressSpace<A>::getP(pint_t logicalAddr) -{ - pint_t relocTarget; - return P::getP(*((pint_t*)mappedAddress(logicalAddr, &relocTarget))) + relocTarget; -} - -template <typename A> -uint64_t ObjectFileAddressSpace<A>::getULEB128(pint_t& logicalAddr, pint_t end) -{ - uintptr_t size = (end - logicalAddr); - libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); - libunwind::LocalAddressSpace::pint_t sladdr = laddr; - uint64_t result = libunwind::LocalAddressSpace::getULEB128(laddr, laddr+size); - logicalAddr += (laddr-sladdr); - return result; -} - -template <typename A> -int64_t ObjectFileAddressSpace<A>::getSLEB128(pint_t& logicalAddr, pint_t end) -{ - uintptr_t size = (end - logicalAddr); - libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); - libunwind::LocalAddressSpace::pint_t sladdr = laddr; - int64_t result = libunwind::LocalAddressSpace::getSLEB128(laddr, laddr+size); - logicalAddr += (laddr-sladdr); - return result; -} - - - - - - -template <typename A> -class Reader : public ObjectFile::Reader -{ -public: - static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0); - static const char* fileKind(const uint8_t* fileContent); - Reader(const uint8_t* fileContent, const char* path, time_t modTime, - const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); - virtual ~Reader() {} - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return fModTime; } - virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return fDebugInfo; } - virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return (std::vector<class ObjectFile::Atom*>&)(fAtoms); } - virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector<Stab>* getStabs() { return &fStabs; } - virtual ObjectFile::Reader::ObjcConstraint getObjCConstraint() { return fObjConstraint; } - virtual uint32_t updateCpuConstraint(uint32_t current); - virtual bool canScatterAtoms() { return (fHeader->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); } - virtual bool objcReplacementClasses(){ return fReplacementClasses; } - virtual bool hasLongBranchStubs() { return fHasLongBranchStubs; } - - bool getTranslationUnitSource(const char** dir, const char** name) const; - -private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - //typedef typename std::vector<Atom<A>*> AtomVector; - //typedef typename AtomVector::iterator AtomVectorIterator; // seems to help C++ parser - typedef typename A::ReferenceKinds Kinds; - typedef typename libunwind::CFI_Parser<ObjectFileAddressSpace<A> >::FDE_Atom_Info FDE_Atom_Info; - typedef typename libunwind::CFI_Parser<ObjectFileAddressSpace<A> >::CIE_Atom_Info CIE_Atom_Info; - typedef class ObjectFileAddressSpace<A> OAS; - friend class ObjectFileAddressSpace<A>; - friend class AnonymousAtom<A>; - friend class TentativeAtom<A>; - friend class AbsoluteAtom<A>; - friend class SectionBoundaryAtom<A>; - friend class SymbolAtom<A>; - typedef std::map<pint_t, BaseAtom*> AddrToAtomMap; - - void addReferencesForSection(const macho_section<P>* sect); - bool addRelocReference(const macho_section<P>* sect, const macho_relocation_info<P>* reloc); - bool addRelocReference_powerpc(const macho_section<P>* sect, const macho_relocation_info<P>* reloc); - const char* getDwarfString(uint64_t form, const uint8_t* p); - bool read_comp_unit(const char ** name, const char ** comp_dir, uint64_t *stmt_list); - static bool isWeakImportSymbol(const macho_nlist<P>* sym); - static bool skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, uint8_t addr_size, bool dwarf64); - static const char* assureFullPath(const char* path); - AtomAndOffset findAtomAndOffset(pint_t addr); - AtomAndOffset findAtomAndOffsetForSection(pint_t addr, unsigned int sectionIndex); - AtomAndOffset findAtomAndOffset(pint_t baseAddr, pint_t realAddr); - Reference<A>* makeReference(Kinds kind, pint_t atAddr, pint_t toAddr); - Reference<A>* makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr); - Reference<A>* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr); - Reference<A>* makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr); - Reference<A>* makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset); - BaseAtom* makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect); - Reference<A>* makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist<P>* toSymbol, pint_t toOffset); - void validSectionType(uint8_t type); - void addDtraceExtraInfos(uint32_t probeAddr, const char* providerName); - void setCpuConstraint(uint32_t cpusubtype); - const macho_section<P>* getSectionForAddress(pint_t); - ObjectFile::Atom* getFunctionAtomFromFDEAddress(pint_t); - ObjectFile::Atom* getFunctionAtomFromLSDAAddress(pint_t); - void addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target); - void addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding); - bool isSectDiffReloc(uint8_t r_type); - - - BaseAtom* findAtomByName(const char*); - - const char* fPath; - time_t fModTime; - uint32_t fOrdinalBase; - const ObjectFile::ReaderOptions& fOptions; - const macho_header<P>* fHeader; - const char* fStrings; - const macho_nlist<P>* fSymbols; - uint32_t fSymbolCount; - const macho_segment_command<P>* fSegment; - const uint32_t* fIndirectTable; - std::vector<BaseAtom*> fAtoms; - AddrToAtomMap fAddrToAtom; - AddrToAtomMap fAddrToAbsoluteAtom; - std::vector<class AnonymousAtom<A>*> fLocalNonLazys; - std::vector<class AnonymousAtom<A>*> fAtomsPendingAName; - std::set<const macho_section<P>*> fSectionsWithAtomsPendingAName; - std::vector<const char*> fDtraceProviderInfo; - ObjectFile::Reader::DebugInfoKind fDebugInfo; - bool fHasUUID; - const macho_section<P>* fehFrameSection; - const macho_section<P>* fUTF16Section; - std::set<BaseAtom*> fLSDAAtoms; - const macho_section<P>* fDwarfDebugInfoSect; - const macho_section<P>* fDwarfDebugAbbrevSect; - const macho_section<P>* fDwarfDebugLineSect; - const macho_section<P>* fDwarfDebugStringSect; - const char* fDwarfTranslationUnitDir; - const char* fDwarfTranslationUnitFile; - std::map<uint32_t,const char*> fDwarfIndexToFile; - std::vector<Stab> fStabs; - std::vector<FDE_Atom_Info> fFDEInfos; - std::vector<CIE_Atom_Info> fCIEInfos; - bool fAppleObjc; - bool fHasDTraceProbes; - bool fHaveIndirectSymbols; - bool fReplacementClasses; - bool fHasLongBranchStubs; - ObjectFile::Reader::ObjcConstraint fObjConstraint; - uint32_t fCpuConstraint; - const macho_section<P>* fSectionsStart; - const macho_section<P>* fSectionsEnd; - OAS fObjectAddressSpace; -}; - -template <typename A> -Reader<A>::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<P>*)fileContent), - fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fSegment(NULL), fIndirectTable(NULL), - fDebugInfo(kDebugInfoNone), fHasUUID(false), fehFrameSection(NULL), fUTF16Section(NULL), - fDwarfDebugInfoSect(NULL), fDwarfDebugAbbrevSect(NULL), fDwarfDebugLineSect(NULL), - fDwarfTranslationUnitDir(NULL), fDwarfTranslationUnitFile(NULL), fAppleObjc(false), fHasDTraceProbes(false), - fHaveIndirectSymbols(false), fReplacementClasses(false), fHasLongBranchStubs(false), - fObjConstraint(ObjectFile::Reader::kObjcNone), fCpuConstraint(ObjectFile::Reader::kCpuAny), - fSectionsStart(NULL), fSectionsEnd(NULL), fObjectAddressSpace(*this) -{ - // sanity check - if ( ! validFile(fileContent, false, 0) ) - throw "not a valid mach-o object file"; - - Reference<A>::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<P>* header = (const macho_header<P>*)fileContent; - this->setCpuConstraint(header->cpusubtype()); - const uint32_t cmd_count = header->ncmds(); - const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>)); - const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds()); - const macho_load_command<P>* cmd = cmds; - uint32_t undefinedStartIndex = 0; - uint32_t undefinedEndIndex = 0; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd; - fSymbolCount = symtab->nsyms(); - fSymbols = (const macho_nlist<P>*)((char*)header + symtab->symoff()); - fStrings = (char*)header + symtab->stroff(); - if ( undefinedEndIndex == 0 ) { - undefinedStartIndex = 0; - undefinedEndIndex = symtab->nsyms(); - } - } - break; - case LC_DYSYMTAB: - { - const macho_dysymtab_command<P>* dsymtab = (struct macho_dysymtab_command<P>*)cmd; - fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff()); - undefinedStartIndex = dsymtab->iundefsym(); - undefinedEndIndex = undefinedStartIndex + dsymtab->nundefsym(); - } - break; - case LC_UUID: - fHasUUID = true; - break; - - default: - if ( cmd->cmd() == macho_segment_command<P>::CMD ) { - fSegment = (macho_segment_command<P>*)cmd; - } - break; - } - cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); - if ( cmd > cmdsEnd ) - throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, path); - } - - // if there are no load commands, then this file has no content, so no atoms - if ( header->ncmds() < 1 ) - return; - - fSectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>)); - fSectionsEnd = &fSectionsStart[fSegment->nsects()]; - - // inital guess for number of atoms - fAtoms.reserve(fSymbolCount); - - // if there is an __eh_frame section, decode it into chunks to get atoms in that - // section as well as division points for functions in __text - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (strcmp(sect->sectname(), "__eh_frame") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) { - fehFrameSection = sect; - const char* msg = libunwind::CFI_Parser<ObjectFileAddressSpace<A> >::getCFIs(fObjectAddressSpace, sect->addr(), - sect->size(), fFDEInfos, fCIEInfos); - if ( msg != NULL ) { - throwf("malformed __eh_frame section: %s", msg); - } - else { - //fprintf(stderr, "%lu CIEs, %lu FDEs\n", fCIEInfos.size(), fFDEInfos.size()); - // add anonymous atoms for each CIE - for (typename std::vector<CIE_Atom_Info>::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { - AnonymousAtom<A>* cieAtom = new AnonymousAtom<A>(*this, sect, it->cieAddress, 1); - fAtoms.push_back(cieAtom); - fAddrToAtom[it->cieAddress] = cieAtom; - } - // add anonymous atoms for each FDE and LSDA - for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - //fprintf(stderr, "fdeAddress=0x%08llX, lsdaAddr=0x%08llX, funcAddr=0x%08llX\n", (uint64_t)it->fdeAddress, (uint64_t)it->lsda.address, (uint64_t)it->function.address); - AnonymousAtom<A>* fdeAtom = new AnonymousAtom<A>(*this, sect, it->fdeAddress, 0); - fAtoms.push_back(fdeAtom); - fAddrToAtom[it->fdeAddress] = fdeAtom; - if ( it->lsda.address != 0 ) { - AnonymousAtom<A>* lsdaAtom = new AnonymousAtom<A>(*this, getSectionForAddress(it->lsda.address), it->lsda.address, 0); - fAtoms.push_back(lsdaAtom); - fAddrToAtom[it->lsda.address] = lsdaAtom; - fLSDAAtoms.insert(lsdaAtom); - } - } - } - } - else if ( (strcmp(sect->sectname(), "__ustring") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) && (sect->size() != 0) ) { - // if there is a __ustring section parse it into atoms - fUTF16Section = sect; - // first find all cleave points - const uint16_t* words = (uint16_t*)((char*)(fHeader) + fUTF16Section->offset()); - unsigned int wordCount = fUTF16Section->size()/2; - std::vector<pint_t> utf16Addreses; - bool inString = false; - for (unsigned int i=0; i < wordCount; ++i) { - if ( inString ) { - if ( words[i] == 0x0000 ) { - inString = false; - } - } - else { - if ( words[i] == 0x0000 ) { - // skip over zero padding - } - else { - inString = true; - utf16Addreses.push_back(fUTF16Section->addr() + i*2); - } - } - } - utf16Addreses.push_back(fUTF16Section->addr() + sect->size()); - // build map of symbols - std::map<pint_t, const macho_nlist<P>* > symbolMap; - for (int i=fSymbolCount-1; i >= 0 ; --i) { - const macho_nlist<P>& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - uint8_t type = (sym.n_type() & N_TYPE); - if ( type == N_SECT ) { - if ( &fSectionsStart[sym.n_sect()-1] == fUTF16Section ) { - // rdar://problem/7429384 don't coalesce UTF16 strings unless label starts with ___utf16_string - if ( strncmp(&fStrings[sym.n_strx()], "___utf16_string", 15) != 0 ) { - symbolMap[sym.n_value()] = &sym; - // <rdar://problem/7516793> if this symbol is a string of just 0x0000, it may not be in utf16Addreses - if ( words[(sym.n_value() - sect->addr())/2] == 0x0000 ) { - for(typename std::vector<pint_t>::iterator sit=utf16Addreses.begin(); sit != utf16Addreses.end(); ++sit) { - if ( *sit == sym.n_value() ) { - // already in utf16Addreses - break; - } - if ( *sit > sym.n_value() ) { - // need to insert - utf16Addreses.insert(sit, sym.n_value()); - break; - } - } - } - } - } - } - } - } - // make atom for each string - for(int i=utf16Addreses.size()-2; i >=0 ; --i) { - pint_t size = utf16Addreses[i+1] - utf16Addreses[i]; - typename std::map<pint_t, const macho_nlist<P>* >::iterator pos = symbolMap.find(utf16Addreses[i]); - if ( pos == symbolMap.end() ) { - AnonymousAtom<A>* strAtom = new AnonymousAtom<A>(*this, fUTF16Section, utf16Addreses[i], size); - fAtoms.push_back(strAtom); - fAddrToAtom[utf16Addreses[i]] = strAtom; - } - else { - SymbolAtom<A>* newAtom = new SymbolAtom<A>(*this, pos->second, fUTF16Section); - fAtoms.push_back(newAtom); - fAddrToAtom[utf16Addreses[i]] = newAtom; - newAtom->setSize(size); - } - } - } - } - - - // add all atoms that have entries in symbol table - BaseAtom* sectionEndAtoms[fSegment->nsects()]; - for (unsigned int i=0; i < fSegment->nsects(); ++i) - sectionEndAtoms[i] = NULL; - for (int i=fSymbolCount-1; i >= 0 ; --i) { - // walk backwards through symbol table so globals are see before locals, otherwise a local alias would beome the real name - const macho_nlist<P>& 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<P>* section = &fSectionsStart[sym.n_sect()-1]; - const pint_t sectionStartAddr = section->addr(); - const pint_t sectionEndAddr = sectionStartAddr + section->size(); - bool suppress = false; - // ignore atoms in debugger sections - if ( (section->flags() & S_ATTR_DEBUG) == 0 ) { - if ( strncmp(&fStrings[sym.n_strx()], "__dtrace_probe$", 15) == 0 ) { - // ignore dtrace probe labels - fHasDTraceProbes = true; - } - else if ( fStrings[sym.n_strx()] == 'L' ) { - // ignore L labels, <rdar://problem/3962731> - } - else if ( section == fehFrameSection ) { - // ignore labels in __eh_frame section - } - else if ( section == fUTF16Section ) { - // ignore labels in __ustring section - } - else { - // ignore labels for atoms in other sections - switch ( section->flags() & SECTION_TYPE ) { - case S_REGULAR: - if ( (sym.n_desc() & N_WEAK_DEF) && strcmp(section->sectname(), "__picsymbolstub1__TEXT") == 0 ) - suppress = true; // ignore stubs in crt1.o built by old ld64 that was missing S_SYMBOL_STUBS - case S_ZEROFILL: - case S_COALESCED: - case S_4BYTE_LITERALS: - case S_8BYTE_LITERALS: - case S_16BYTE_LITERALS: - case S_CSTRING_LITERALS: - { - BaseAtom* newAtom; - typename AddrToAtomMap::iterator pos = fAddrToAtom.find(sym.n_value()); - if ( (pos != fAddrToAtom.end()) && (pos->second->getSectionRecord() == section) ) { - if ( fLSDAAtoms.count(pos->second) != 0 ) { - // already have LSDA atom from for this address, ignore compiler's label - suppress = true; - break; - } - else { - // another label to an existing address in the same section, make this an alias - newAtom = new SymbolAliasAtom<A>(&fStrings[sym.n_strx()], &sym, *pos->second); - } - } - else { - if ( sym.n_value() == sectionEndAddr ) { - // Symbol address is at end of section. This can interfere - // with a symbol at the start of the next section, so don't - // add to fAddrToAtom. But do track in sectionEndAtoms so we - // still make aliases if there are duplicates. - if ( sectionEndAtoms[sym.n_sect()-1] == NULL ) { - newAtom = new SymbolAtom<A>(*this, &sym, section); - sectionEndAtoms[sym.n_sect()-1] = newAtom; - // if this is a zero length section, so add to fAddrToAtom - if ( sym.n_value() == sectionStartAddr ) - fAddrToAtom[newAtom->getObjectAddress()] = newAtom; - } - else { - newAtom = new SymbolAliasAtom<A>(&fStrings[sym.n_strx()], &sym, *sectionEndAtoms[sym.n_sect()-1]); - } - } - else { - // make SymbolAtom atom for this address - newAtom = new SymbolAtom<A>(*this, &sym, section); - fAddrToAtom[newAtom->getObjectAddress()] = newAtom; - } - } - if ( ! suppress ) - fAtoms.push_back(newAtom); - } - break; - case S_SYMBOL_STUBS: - case S_LAZY_SYMBOL_POINTERS: - case S_NON_LAZY_SYMBOL_POINTERS: - // ignore symboled stubs produces by old ld64 - break; - default: - warning("symbol %s found in unsupported section in %s", - &fStrings[sym.n_strx()], this->getPath()); - } - } - } - } - else if ( (type == N_UNDF) && (sym.n_value() != 0) ) { - fAtoms.push_back(new TentativeAtom<A>(*this, &sym)); - } - else if ( (type == N_UNDF) && (sym.n_value() == 0) ) { - const char* symName = &fStrings[sym.n_strx()]; - if ( strncmp(symName, "section$start$", 14) == 0) - fAtoms.push_back(new SectionBoundaryAtom<A>(*this, true, symName, &symName[14])); - else if ( strncmp(symName, "section$end$", 12) == 0) - fAtoms.push_back(new SectionBoundaryAtom<A>(*this, false, symName, &symName[12])); - } - else if ( type == N_ABS ) { - const char* symName = &fStrings[sym.n_strx()]; - if ( strncmp(symName, ".objc_class_name_", 17) == 0 ) { - // ignore .objc_class_name_* symbols - fAppleObjc = true; - } - else if ( strcmp(&symName[strlen(symName)-3], ".eh") == 0 ) { - // ignore empty *.eh symbols - } - else { - BaseAtom* abAtom = new AbsoluteAtom<A>(*this, &sym); - fAtoms.push_back(abAtom); - fAddrToAbsoluteAtom[sym.n_value()] = abAtom; - } - } - else if ( type == N_INDR ) { - fHaveIndirectSymbols = true; - } - } - } - - // add anonymous atoms for any functions (as determined by dwarf unwind) have no symbol names - if ( fehFrameSection != NULL ) { - for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - // add if not already an atom at that address - if ( fAddrToAtom.find(it->function.address) == fAddrToAtom.end() ) { - AnonymousAtom<A>* funcAtom = new AnonymousAtom<A>(*this, getSectionForAddress(it->function.address), it->function.address, 0); - fAtoms.push_back(funcAtom); - fAddrToAtom[it->function.address] = funcAtom; - // even though we've made a new atom, be conservative and make sure they lay out together - if ( canScatterAtoms() ) { - AtomAndOffset prev = findAtomAndOffset(it->function.address-1); - if ( prev.atom != NULL ) { - if ( ((BaseAtom*)(prev.atom))->getSectionRecord() == funcAtom->getSectionRecord() ) - new Reference<A>(A::kFollowOn, prev, AtomAndOffset(funcAtom)); - } - } - } - } - } - - - // add all fixed size anonymous atoms from special sections - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - pint_t atomSize = 0; - uint8_t type (sect->flags() & SECTION_TYPE); - validSectionType(type); - bool suppress = false; - switch ( type ) { - case S_SYMBOL_STUBS: - suppress = true; - atomSize = sect->reserved2(); - break; - case S_LAZY_SYMBOL_POINTERS: - suppress = true; - atomSize = sizeof(pint_t); - break; - case S_NON_LAZY_SYMBOL_POINTERS: - case S_LITERAL_POINTERS: - case S_MOD_INIT_FUNC_POINTERS: - case S_MOD_TERM_FUNC_POINTERS: - atomSize = sizeof(pint_t); - break; - case S_INTERPOSING: - atomSize = sizeof(pint_t)*2; - break; - case S_4BYTE_LITERALS: - atomSize = 4; - break; - case S_8BYTE_LITERALS: - atomSize = 8; - break; - case S_16BYTE_LITERALS: - atomSize = 16; - break; - case S_REGULAR: - // special case ObjC classes to synthesize .objc_class_name_* symbols - if ( (strcmp(sect->sectname(), "__class") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) && fAppleObjc ) { - // gcc sometimes over aligns class structure - uint32_t align = 1 << sect->align(); - atomSize = ((12 * sizeof(pint_t)) + align-1) & (-align); - } - // get objc Garbage Collection info - else if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0)) - || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) { - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - const uint32_t* contents = (uint32_t*)(((char*)fHeader) + sect->offset()); - if ( (sect->size() >= 8) && (contents[0] == 0) ) { - uint32_t flags = E::get32(contents[1]); - if ( (flags & 4) == 4 ) - fObjConstraint = ObjectFile::Reader::kObjcGC; - else if ( (flags & 2) == 2 ) - fObjConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - else - fObjConstraint = ObjectFile::Reader::kObjcRetainRelease; - if ( (flags & 1) == 1 ) - fReplacementClasses = true; - // don't make atom for this section - atomSize = sect->size(); - suppress = true; - } - else { - warning("can't parse __OBJC/__image_info section in %s", fPath); - } - } - // special case constant NS/CFString literals and make an atom out of each one - else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) { - atomSize = 4 * sizeof(pint_t); - } - // special case class reference sections - else if ( (strncmp(sect->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0) ) { - atomSize = sizeof(pint_t);; - } - break; - } - if ( atomSize != 0 ) { - for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += atomSize) { - pint_t atomAddr = sect->addr() + sectOffset; - // add if not already an atom at that address - if ( fAddrToAtom.find(atomAddr) == fAddrToAtom.end() ) { - AnonymousAtom<A>* newAtom = new AnonymousAtom<A>(*this, sect, atomAddr, atomSize); - if ( !suppress ) - fAtoms.push_back(newAtom); - fAddrToAtom[atomAddr] = newAtom->redirectTo(); - } - } - } - } - - // add all c-string anonymous atoms - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( ((sect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS) || strcmp(sect->sectname(), "__cstring") == 0 ) { - uint32_t stringLen; - pint_t stringAddr; - BaseAtom* mostAlignedEmptyString = NULL; - uint32_t mostAlignedEmptyStringTrailingZeros = 0; - std::vector<std::pair<pint_t,BaseAtom*> > emptyStrings; - for(pint_t sectOffset=0; sectOffset < sect->size(); sectOffset += stringLen) { - stringAddr = sect->addr() + sectOffset; - stringLen = strlen((char*)(fHeader) + sect->offset() + sectOffset) + 1; - // add if not already a non-zero length atom at that address - typename AddrToAtomMap::iterator pos = fAddrToAtom.find(stringAddr); - if ( (pos == fAddrToAtom.end()) || (pos->second->getSize() == 0) ) { - BaseAtom* newAtom = new AnonymousAtom<A>(*this, sect, stringAddr, stringLen); - if ( stringLen == 1 ) { - // because of padding it may look like there are lots of empty strings, keep track of all - emptyStrings.push_back(std::make_pair<pint_t,BaseAtom*>(stringAddr, newAtom)); - // record empty string with greatest alignment requirement - uint32_t stringAddrTrailingZeros = (stringAddr==0) ? sect->align() : __builtin_ctz(stringAddr); - if ( (mostAlignedEmptyString == NULL) - || ( stringAddrTrailingZeros > mostAlignedEmptyStringTrailingZeros) ) { - mostAlignedEmptyString = newAtom; - mostAlignedEmptyStringTrailingZeros = stringAddrTrailingZeros; - } - } - else { - fAtoms.push_back(newAtom); - fAddrToAtom[stringAddr] = newAtom; - } - } - } - // map all uses of empty strings to the most aligned one - if ( mostAlignedEmptyString != NULL ) { - // make most aligned atom a real atom - fAtoms.push_back(mostAlignedEmptyString); - // map all other empty atoms to this one - for (typename std::vector<std::pair<pint_t,BaseAtom*> >::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<BaseAtom*>::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<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - pint_t sectionStartAddr = sect->addr(); - pint_t sectionEndAddr = sect->addr() + sect->size(); - // don't set follow-on atoms in __eh_frame section - const bool setFollowOnAtom = !canScatterAtoms() && (sect != fehFrameSection); - if ( sect->size() != 0 ) { - // ignore dwarf sections. If ld every supports processing dwarf, this logic will need to change - if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { - fDebugInfo = kDebugInfoDwarf; - if ( strcmp(sect->sectname(), "__debug_info") == 0 ) - fDwarfDebugInfoSect = sect; - else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) - fDwarfDebugAbbrevSect = sect; - else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) - fDwarfDebugLineSect = sect; - else if ( strcmp(sect->sectname(), "__debug_str") == 0 ) - fDwarfDebugStringSect = sect; - } - else { - if ( strcmp(sect->segname(), "__DWARFA") == 0 ) { - throw "object file contains old DWARF debug info - rebuild with newer compiler"; - } - uint8_t type (sect->flags() & SECTION_TYPE); - switch ( type ) { - case S_REGULAR: - case S_ZEROFILL: - case S_COALESCED: - // if there is not an atom already at the start of this section, add an anonymous one - pint_t previousAtomAddr = 0; - BaseAtom* previousAtom = NULL; - if ( fAddrToAtom.find(sectionStartAddr) == fAddrToAtom.end() ) { - BaseAtom* newAtom = new AnonymousAtom<A>(*this, sect, sect->addr(), 0); - fAddrToAtom[sect->addr()] = newAtom; - fAtoms.push_back(newAtom); - previousAtomAddr = sectionStartAddr; - previousAtom = newAtom; - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - } - // calculate size of all atoms in this section and add follow-on references - for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - pint_t atomAddr = atom->getObjectAddress(); - if ( atom->getSectionRecord() == sect ) { - //fprintf(stderr, "addr=0x%08llX, atom=%s\n", (uint64_t)atomAddr, atom->getDisplayName()); - if ( (previousAtom != NULL) && (previousAtomAddr != atomAddr) ) { - previousAtom->setSize(atomAddr - previousAtomAddr); - if ( setFollowOnAtom && (atom != previousAtom) ) - new Reference<A>(A::kFollowOn, AtomAndOffset(previousAtom), AtomAndOffset(atom)); - } - previousAtomAddr = atomAddr; - previousAtom = atom; - } - } - if ( previousAtom != NULL ) { - // set last atom in section - previousAtom->setSize(sectionEndAddr - previousAtomAddr); - } - break; - } - } - } - } - - // check for object file that defines no objc classes, but uses objc classes - // check for dtrace provider info - for (uint32_t i=undefinedStartIndex; i < undefinedEndIndex; ++i) { - const macho_nlist<P>& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_UNDF ) { - const char* undefinedName = &fStrings[sym.n_strx()]; - if ( !fAppleObjc && (strncmp(undefinedName, ".objc_class_name_", 17) == 0) ) { - fAppleObjc = true; - } - else if ( strncmp(undefinedName, "___dtrace_", 10) == 0 ) { - if ( strchr(undefinedName, '$') != NULL ) { - if ( (strncmp(&undefinedName[10], "probe$", 6) != 0) && (strncmp(&undefinedName[10], "isenabled$", 10) != 0) ) { - // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$* - // is extra provider info - fDtraceProviderInfo.push_back(undefinedName); - } - } - } - } - } - } - - // add relocation based references to sections that have atoms with pending names - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( fSectionsWithAtomsPendingAName.count(sect) != 0 ) - addReferencesForSection(sect); - } - - // update any anonymous atoms that need references built in order to name themselves - for (typename std::vector<AnonymousAtom<A>*>::iterator it=fAtomsPendingAName.begin(); it != fAtomsPendingAName.end(); it++) { - (*it)->resolveName(); - } - - // add relocation based references to other sections - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( fSectionsWithAtomsPendingAName.count(sect) == 0 ) - addReferencesForSection(sect); - } - - // add objective-c references - if ( fAppleObjc ) { - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (strcmp(sect->sectname(), "__cls_refs") == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) { - for (uint32_t offset = 0; offset < sect->size(); offset += sizeof(pint_t)) { - AtomAndOffset ao = this->findAtomAndOffset(sect->addr()+offset); - ObjectFile::Reference* classRef = ao.atom->getReferences()[0]; - if ( classRef->getFixUpOffset() == 0 ) { - const char* classStr = classRef->getTargetName(); - if ( strncmp(classStr, "cstring=", 8) == 0 ) { - const char* className; - asprintf((char**)&className, ".objc_class_name_%s", &classStr[8]); - new Reference<A>(A::kNoFixUp, ao, className, 0); - } - } - } - } - } - } - - // add direct references to local non-lazy-pointers, can do this now that all atoms are constructed - for (typename std::vector<AnonymousAtom<A>*>::iterator it=fLocalNonLazys.begin(); it != fLocalNonLazys.end(); it++) { - AnonymousAtom<A>* localNonLazy = *it; - uint32_t fileOffset = localNonLazy->fSection->offset() - localNonLazy->fSection->addr() + localNonLazy->fAddress; - pint_t nonLazyPtrValue = P::getP(*((pint_t*)((char*)(fHeader)+fileOffset))); - makeReference(A::kPointer, localNonLazy->fAddress, nonLazyPtrValue); - } - - - // add personality references to CIEs - for (typename std::vector<CIE_Atom_Info>::const_iterator it = fCIEInfos.begin(); it != fCIEInfos.end(); ++it) { - if ( it->personality.offsetInFDE != 0 ) - addCiePersonalityReference(fAddrToAtom[it->cieAddress], it->personality.offsetInFDE, it->personality.encodingOfAddress); - } - - // add all references for FDEs, including implicit group references - for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - AtomAndOffset funcAO = this->findAtomAndOffset(it->function.address); - if ( funcAO.offset != 0 ) - warning("FDE does not point to start of function %s\n", funcAO.atom->getDisplayName()); - AtomAndOffset fdeAO = this->findAtomAndOffset(it->fdeAddress); - if ( fdeAO.offset != 0 ) - warning("FDE does start its own atom %s\n", funcAO.atom->getDisplayName()); - AtomAndOffset cieAO = this->findAtomAndOffset(it->cie.address); - if ( cieAO.offset != 0 ) - warning("CIE does start its own atom %s\n", cieAO.atom->getDisplayName()); - AtomAndOffset lsdaAO; - if ( it->lsda.address != 0 ) { - lsdaAO = this->findAtomAndOffset(it->lsda.address); - if ( lsdaAO.offset != 0 ) - warning("LSDA does start its own atom %s\n", lsdaAO.atom->getDisplayName()); - } - - // add reference from FDE to CIE - AtomAndOffset cieInfdeAO = AtomAndOffset(fdeAO.atom, it->cie.offsetInFDE); - new Reference<A>(A::kPointerDiff32, cieInfdeAO, cieAO, cieInfdeAO); - - // add reference from FDE to function - addFdeReference(it->function.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->function.offsetInFDE), funcAO); - - // add reference from FDE to LSDA - if ( it->lsda.address != 0 ) { - addFdeReference(it->lsda.encodingOfAddress, AtomAndOffset(fdeAO.atom, it->lsda.offsetInFDE), lsdaAO); - } - - // FDE is in group lead by function atom - new Reference<A>(A::kGroupSubordinate, funcAO, fdeAO); - - // LSDA is in group lead by function atom - if ( it->lsda.address != 0 ) { - new Reference<A>(A::kGroupSubordinate, funcAO, lsdaAO); - // add back reference from LSDA to owning function - new Reference<A>(A::kNoFixUp, lsdaAO, funcAO); - } - - // compute compact encoding for this FDE - if ( fOptions.fAddCompactUnwindEncoding ) { - ((BaseAtom*)(funcAO.atom))->setCompactUnwindEncoding(it->fdeAddress); - // add reference from function atom to personality function - // the only reference a CIE can have is the reference to the personality function - std::vector<class ObjectFile::Reference*>& cieRefs = cieAO.atom->getReferences(); - if ( cieRefs.size() == 1 ) { - new Reference<A>((typename A::ReferenceKinds)((BaseAtom*)(funcAO.atom))->getPersonalityReferenceKind(), - funcAO, cieRefs[0]->getTargetName(), 0); - } - } - } - - // add command line aliases - for(std::vector<ObjectFile::ReaderOptions::AliasPair>::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<A>(it->alias, NULL, *target)); - } - - // add dtrace probe locations - if ( fHasDTraceProbes ) { - for (uint32_t i=0; i < fSymbolCount; ++i) { - const macho_nlist<P>& sym = fSymbols[i]; - if ( (sym.n_type() & N_STAB) == 0 ) { - if ( (sym.n_type() & N_TYPE) == N_SECT ) { - const char* symbolName = &fStrings[sym.n_strx()]; - if ( strncmp(symbolName, "__dtrace_probe$", 15) == 0 ) { - //fprintf(stderr, "adding dtrace probe at 0x%08llX %s\n", sym.n_value(), symbolName); - makeByNameReference(A::kDtraceProbe, sym.n_value(), symbolName, 0); - } - } - } - } - } - - // turn indirect symbols into SymbolAliasAtom - if ( fHaveIndirectSymbols ) { - for (uint32_t i=0; i < fSymbolCount; ++i) { - const macho_nlist<P>& 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<A>(aliasName, &sym, *target)); - //fprintf(stderr, "creating alias %s for %s\n", aliasName, targetName); - } - } - } - } - } - - //for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - // fprintf(stderr, "[0x%0X -> 0x%0llX) : %s\n", it->first, it->first+it->second->getSize(), it->second->getDisplayName()); - //} - - // add translation unit info from dwarf - uint64_t stmtList; - if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // compiler sometimes emits emtpty dwarf sections when there is no debug info, skip those - if ( (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { - if ( !read_comp_unit(&fDwarfTranslationUnitFile, &fDwarfTranslationUnitDir, &stmtList) ) { - // if can't parse dwarf, warn and give up - fDwarfTranslationUnitFile = NULL; - fDwarfTranslationUnitDir = NULL; - warning("can't parse dwarf compilation unit info in %s", this->getPath()); - fDebugInfo = kDebugInfoNone; - } - } - } - - // add line number info to atoms from dwarf - if ( (fDebugInfo == kDebugInfoDwarf) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // file with just data will have no __debug_line info - if ( (fDwarfDebugLineSect != NULL) && (fDwarfDebugLineSect->size() != 0) && (fAddrToAtom.size() != 0) - && (fDwarfDebugInfoSect != NULL) && (fDwarfDebugInfoSect->size() != 0) ) { - // validate stmt_list - if ( (stmtList != (uint64_t)-1) && (stmtList < fDwarfDebugLineSect->size()) ) { - const uint8_t* debug_line = (uint8_t*)(fHeader) + fDwarfDebugLineSect->offset(); - if ( debug_line != NULL ) { - struct line_reader_data* lines = line_open(&debug_line[stmtList], - fDwarfDebugLineSect->size() - stmtList, E::little_endian); - struct line_info result; - ObjectFile::Atom* curAtom = NULL; - uint32_t curAtomOffset = 0; - uint32_t curAtomAddress = 0; - uint32_t curAtomSize = 0; - if ( lines != NULL ) { - while ( line_next (lines, &result, line_stop_pc) ) { - //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d, curAtomAddress=0x%X, curAtomSize=0x%X\n", - // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); - // work around weird debug line table compiler generates if no functions in __text section - if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) - continue; - // for performance, see if in next pc is in current atom - if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - // or pc at end of current atom - else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { - curAtomOffset = result.pc - curAtomAddress; - } - else { - // do slow look up of atom by address - AtomAndOffset ao = this->findAtomAndOffset(result.pc); - curAtom = ao.atom; - if ( curAtom == NULL ) - break; // file has line info but no functions - if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { - // a one line function can be returned by line_next() as one entry with pc at end of blob - // look for alt atom starting at end of previous atom - uint32_t previousEnd = curAtomAddress+curAtomSize; - AtomAndOffset alt = this->findAtomAndOffset(previousEnd); - if ( result.pc <= previousEnd - alt.offset + alt.atom->getSize() ) { - curAtom = alt.atom; - curAtomOffset = alt.offset; - curAtomAddress = previousEnd - alt.offset; - curAtomSize = curAtom->getSize(); - } - else { - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); - } - } - else { - curAtomOffset = ao.offset; - curAtomAddress = result.pc - ao.offset; - curAtomSize = curAtom->getSize(); - } - } - const char* filename; - std::map<uint32_t,const char*>::iterator pos = fDwarfIndexToFile.find(result.file); - if ( pos == fDwarfIndexToFile.end() ) { - filename = line_file(lines, result.file); - fDwarfIndexToFile[result.file] = filename; - } - else { - filename = pos->second; - } - ObjectFile::LineInfo info; - info.atomOffset = curAtomOffset; - info.fileName = filename; - info.lineNumber = result.line; - //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", - // result.pc, result.line, filename, curAtom->getDisplayName(), curAtomSize, result.end_of_sequence); - ((BaseAtom*)curAtom)->addLineInfo(info); - if ( result.end_of_sequence ) { - curAtom = NULL; - } - } - line_free(lines); - } - } - else { - warning("could not parse dwarf line number info in %s", this->getPath()); - } - } - } - } - - // if no dwarf, try processing stabs debugging info - if ( (fDebugInfo == kDebugInfoNone) && (fOptions.fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone) ) { - // scan symbol table for stabs entries - fStabs.reserve(fSymbolCount); // reduce re-allocations - BaseAtom* currentAtom = NULL; - pint_t currentAtomAddress = 0; - enum { start, inBeginEnd, inFun } state = start; - for (uint32_t symbolIndex = 0; symbolIndex < fSymbolCount; ++symbolIndex ) { - const macho_nlist<P>* sym = &fSymbols[symbolIndex]; - bool useStab = true; - uint8_t type = sym->n_type(); - const char* symString = (sym->n_strx() != 0) ? &fStrings[sym->n_strx()] : NULL; - if ( (type & N_STAB) != 0 ) { - fDebugInfo = (fHasUUID ? kDebugInfoStabsUUID : kDebugInfoStabs); - Stab stab; - stab.atom = NULL; - stab.type = type; - stab.other = sym->n_sect(); - stab.desc = sym->n_desc(); - stab.value = sym->n_value(); - stab.string = NULL; - switch (state) { - case start: - switch (type) { - case N_BNSYM: - // beginning of function block - state = inBeginEnd; - // fall into case to lookup atom by addresss - case N_LCSYM: - case N_STSYM: - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s", - (uint64_t)sym->n_value(), path); - } - break; - case N_SO: - case N_OSO: - case N_OPT: - case N_LSYM: - case N_RSYM: - case N_PSYM: - // not associated with an atom, just copy - stab.string = symString; - break; - case N_GSYM: - { - // n_value field is NOT atom address ;-( - // need to find atom by name match - const char* colon = strchr(symString, ':'); - if ( colon != NULL ) { - // build underscore leading name - int nameLen = colon - symString; - char symName[nameLen+2]; - strlcpy(&symName[1], symString, nameLen+1); - symName[0] = '_'; - symName[nameLen+1] = '\0'; - currentAtom = findAtomByName(symName); - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - } - else { - // might be a debug-note without trailing :G() - currentAtom = findAtomByName(symString); - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - } - if ( stab.atom == NULL ) { - // ld_classic added bogus GSYM stabs for old style dtrace probes - if ( (strncmp(symString, "__dtrace_probe$", 15) != 0) ) - warning("can't find atom for N_GSYM stabs %s in %s", symString, path); - useStab = false; - } - break; - } - case N_FUN: - // old style stabs without BNSYM - state = inFun; - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs FUN at %08llX in %s", - (uint64_t)currentAtomAddress, path); - } - break; - case N_SOL: - case N_SLINE: - stab.string = symString; - // old stabs - break; - case N_BINCL: - case N_EINCL: - case N_EXCL: - stab.string = symString; - // -gfull built .o file - break; - default: - warning("unknown stabs type 0x%X in %s", type, path); - } - break; - case inBeginEnd: - stab.atom = currentAtom; - switch (type) { - case N_ENSYM: - state = start; - currentAtom = NULL; - break; - case N_LCSYM: - case N_STSYM: - { - BaseAtom* nestedAtom = (BaseAtom*)this->findAtomAndOffset(sym->n_value()).atom; - if ( nestedAtom != NULL ) { - stab.atom = nestedAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs 0x%X at %08llX in %s", - type, (uint64_t)sym->n_value(), path); - } - break; - } - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - // adjust value to be offset in atom - stab.value -= currentAtomAddress; - default: - stab.string = symString; - break; - } - break; - case inFun: - switch (type) { - case N_FUN: - if ( sym->n_sect() != 0 ) { - // found another start stab, must be really old stabs... - currentAtomAddress = sym->n_value(); - currentAtom = (BaseAtom*)this->findAtomAndOffset(currentAtomAddress).atom; - if ( currentAtom != NULL ) { - stab.atom = currentAtom; - stab.string = symString; - } - else { - warning("can't find atom for stabs FUN at %08llX in %s", - (uint64_t)currentAtomAddress, path); - } - } - else { - // found ending stab, switch back to start state - stab.string = symString; - stab.atom = currentAtom; - state = start; - currentAtom = NULL; - } - break; - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - // adjust value to be offset in atom - stab.value -= currentAtomAddress; - stab.atom = currentAtom; - break; - case N_SO: - stab.string = symString; - state = start; - break; - default: - stab.atom = currentAtom; - stab.string = symString; - break; - } - break; - } - // add to list of stabs for this .o file - if ( useStab ) - fStabs.push_back(stab); - } - } - } - -#if 0 - // special case precompiled header .o file (which has no content) to have one empty atom - if ( fAtoms.size() == 0 ) { - int pathLen = strlen(path); - if ( (pathLen > 6) && (strcmp(&path[pathLen-6], ".gch.o")==0) ) { - ObjectFile::Atom* phony = new AnonymousAtom<A>(*this, (uint32_t)0); - //phony->fSynthesizedName = ".gch.o"; - fAtoms.push_back(phony); - } - } -#endif - - // sort all atoms by address - std::sort(fAtoms.begin(), fAtoms.end(), BaseAtomSorter()); - - // set ordinal and sort references in each atom - uint32_t index = fOrdinalBase; - for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - atom->setOrdinal(index++); - atom->sortReferences(); - } - -} - -template <typename A> -const macho_section<typename A::P>* Reader<A>::getSectionForAddress(pint_t addr) -{ - for (const macho_section<P>* sect=fSectionsStart; sect < fSectionsEnd; ++sect) { - if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) - return sect; - } - throwf("section not found for address 0x%08llX", (uint64_t)addr); -} - -template <typename A> -ObjectFile::Atom* Reader<A>::getFunctionAtomFromFDEAddress(pint_t addr) -{ - for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - if ( it->fdeAddress == addr ) { - return findAtomAndOffset(it->function.address).atom; - } - } - // CIEs won't be in fFDEInfos - return NULL; -} - -template <typename A> -ObjectFile::Atom* Reader<A>::getFunctionAtomFromLSDAAddress(pint_t addr) -{ - for (typename std::vector<FDE_Atom_Info>::const_iterator it = fFDEInfos.begin(); it != fFDEInfos.end(); ++it) { - if ( it->lsda.address == addr ) { - return findAtomAndOffset(it->function.address).atom; - } - } - return NULL; -} - - -template <> -void ObjectFileAddressSpace<x86_64>::buildRelocatedMap(const macho_section<P>* sect, std::map<uint32_t,uint64_t>& map) -{ - // mach-o x86_64 is different, the content of a section with a relocation is the addend - const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fReader.fHeader) + sect->reloff()); - const macho_relocation_info<P>* relocsEnd = &relocs[sect->nreloc()]; - for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) { - std::map<uint32_t,uint64_t>::iterator pos; - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - pos = map.find(reloc->r_address()); - if ( pos != map.end() ) - pos->second += fReader.fSymbols[reloc->r_symbolnum()].n_value(); - else - map[reloc->r_address()] = fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_SUBTRACTOR: - map[reloc->r_address()] = -fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_GOT: - // there is no good address to return here. - // GOT slots are synthsized by the linker - // this is used for the reference to the personality function in CIEs - map[reloc->r_address()] = 0; - break; - default: - fprintf(stderr, "ObjectFileAddressSpace::buildRelocatedMap() unexpected relocation at r_address=0x%08X\n", reloc->r_address()); - break; - } - } -} - -template <typename A> -void ObjectFileAddressSpace<A>::buildRelocatedMap(const macho_section<P>* sect, std::map<uint32_t,uint64_t>& map) -{ - // in all architectures except x86_64, the section contents are already fixed up to point - // to content in the same object file. -} - -template <> -uint64_t ObjectFileAddressSpace<x86_64>::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) -{ - // mach-o x86_64 is different, the content of a section with a relocation is the addend - uint64_t result = 0; - const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fReader.fHeader) + relocsOffset); - const macho_relocation_info<P>* relocsEnd = &relocs[relocsCount]; - for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) { - //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X), r_address=0x%08X\n", sectOffset, reloc->r_address()); - if ( reloc->r_address() == sectOffset ) { - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - result += fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_SUBTRACTOR: - result -= fReader.fSymbols[reloc->r_symbolnum()].n_value(); - break; - case X86_64_RELOC_GOT: - // there is no good address to return here. - // GOT slots are synthsized by the linker - // this is used for the reference to the personality function in CIEs - result = 0; - break; - default: - fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => type=%d, value=0x%08X\n", sectOffset, reloc->r_type(), reloc->r_symbolnum()); - break; - } - } - } - //fprintf(stderr, "ObjectFileAddressSpace::relocated(0x%08X) => 0x%0llX\n", sectOffset, result); - return result; -} - -template <typename A> -typename A::P::uint_t ObjectFileAddressSpace<A>::relocated(uint32_t sectOffset, uint32_t relocsOffset, uint32_t relocsCount) -{ - // in all architectures except x86_64, the section contents are already fixed up to point - // to content in the same object file. - return 0; -} - - - -// FSF exception handling Pointer-Encoding constants -// Used in CFI augmentation by gcc compiler -enum { - DW_EH_PE_ptr = 0x00, - DW_EH_PE_uleb128 = 0x01, - DW_EH_PE_udata2 = 0x02, - DW_EH_PE_udata4 = 0x03, - DW_EH_PE_udata8 = 0x04, - DW_EH_PE_signed = 0x08, - DW_EH_PE_sleb128 = 0x09, - DW_EH_PE_sdata2 = 0x0A, - DW_EH_PE_sdata4 = 0x0B, - DW_EH_PE_sdata8 = 0x0C, - DW_EH_PE_absptr = 0x00, - DW_EH_PE_pcrel = 0x10, - DW_EH_PE_textrel = 0x20, - DW_EH_PE_datarel = 0x30, - DW_EH_PE_funcrel = 0x40, - DW_EH_PE_aligned = 0x50, - DW_EH_PE_indirect = 0x80, - DW_EH_PE_omit = 0xFF -}; - -template <> -void Reader<x86_64>::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding) -{ - if ( encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4) ) - throw "unexpected personality encoding in CIE"; - - // walk relocs looking for reloc in this CIE - uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr(); - const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + fehFrameSection->reloff()); - const macho_relocation_info<P>* relocsEnd = &relocs[fehFrameSection->nreloc()]; - for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) { - if ( reloc->r_address() == sectOffset ) { - switch ( reloc->r_type() ) { - case X86_64_RELOC_GOT: - if ( !reloc->r_extern() ) - throw "GOT reloc not extern for personality function"; - new Reference<x86_64>(x86_64::kPCRel32GOT, AtomAndOffset(cieAtom, offsetInCIE), &fStrings[fSymbols[reloc->r_symbolnum()].n_strx()], 4); - return; - default: - throw "expected GOT reloc for personality function"; - } - } - } - throw "personality function not found for CIE"; -} - -template <> -bool Reader<ppc>::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case PPC_RELOC_LOCAL_SECTDIFF: - case PPC_RELOC_SECTDIFF: - return true; - } - return false; -} - -template <> -bool Reader<ppc64>::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case PPC_RELOC_LOCAL_SECTDIFF: - case PPC_RELOC_SECTDIFF: - return true; - } - return false; -} - -template <> -bool Reader<x86>::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case GENERIC_RELOC_LOCAL_SECTDIFF: - case GENERIC_RELOC_SECTDIFF: - return true; - } - return false; -} - -template <> -bool Reader<arm>::isSectDiffReloc(uint8_t r_type) -{ - switch ( r_type ) { - case ARM_RELOC_LOCAL_SECTDIFF: - case ARM_RELOC_SECTDIFF: - return true; - } - return false; -} - -template <typename A> -void Reader<A>::addCiePersonalityReference(BaseAtom* cieAtom, uint32_t offsetInCIE, uint8_t encoding) -{ - if ( (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4)) && (encoding != (DW_EH_PE_indirect|DW_EH_PE_pcrel)) ) - throw "unexpected personality encoding in CIE"; - - // walk relocs looking for personality reloc in this CIE - uint32_t sectOffset = (cieAtom->getObjectAddress() + offsetInCIE) - fehFrameSection->addr(); - const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + fehFrameSection->reloff()); - const macho_relocation_info<P>* relocsEnd = &relocs[fehFrameSection->nreloc()]; - for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) { - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - // ignore - } - else { - const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; - if ( sreloc->r_address() == sectOffset ) { - if ( isSectDiffReloc(sreloc->r_type()) ) { - // r_value is address of non-lazy-pointer to personality function - new Reference<A>(A::kPointerDiff32, AtomAndOffset(cieAtom, offsetInCIE), AtomAndOffset(cieAtom, offsetInCIE), - findAtomAndOffset(sreloc->r_value())); - return; - } - } - } - } - throw "can't find relocation for personality in CIE"; -} - -template <typename A> -void Reader<A>::addFdeReference(uint8_t encoding, AtomAndOffset inFDE, AtomAndOffset target) -{ - if ( (encoding & 0xF0) != DW_EH_PE_pcrel ) - throw "unsupported encoding in FDE"; - Kinds kind = A::kNoFixUp; - switch ( encoding & 0xF ) { - case DW_EH_PE_ptr: - kind = A::kPointerDiff; - break; - case DW_EH_PE_sdata4: - kind = A::kPointerDiff32; - break; - default: - throw "unsupported encoding in FDE"; - } - new Reference<A>(kind, inFDE, inFDE, target); -} - -template <typename A> -typename A::P::uint_t ObjectFileAddressSpace<A>::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) -{ - pint_t startAddr = addr; - pint_t p = addr; - pint_t result; - - // first get value - switch (encoding & 0x0F) { - case DW_EH_PE_ptr: - result = getP(addr); - p += sizeof(pint_t); - addr = (pint_t)p; - break; - case DW_EH_PE_uleb128: - result = getULEB128(addr, end); - break; - case DW_EH_PE_udata2: - result = get16(addr); - p += 2; - addr = (pint_t)p; - break; - case DW_EH_PE_udata4: - result = get32(addr); - p += 4; - addr = (pint_t)p; - break; - case DW_EH_PE_udata8: - result = get64(addr); - p += 8; - addr = (pint_t)p; - break; - case DW_EH_PE_sleb128: - result = getSLEB128(addr, end); - break; - case DW_EH_PE_sdata2: - result = (int16_t)get16(addr); - p += 2; - addr = (pint_t)p; - break; - case DW_EH_PE_sdata4: - result = (int32_t)get32(addr); - p += 4; - addr = (pint_t)p; - break; - case DW_EH_PE_sdata8: - result = get64(addr); - p += 8; - addr = (pint_t)p; - break; - default: - throwf("ObjectFileAddressSpace<A>::getEncodedP() encoding 0x%08X not supported", encoding); - } - - // then add relative offset - switch ( encoding & 0x70 ) { - case DW_EH_PE_absptr: - // do nothing - break; - case DW_EH_PE_pcrel: - // <rdar://problem/7200658> pc-rel sdata4 should return zero if content is zero - if ( (result != 0) || ((encoding & DW_EH_PE_indirect) != 0) ) - result += startAddr; - break; - case DW_EH_PE_textrel: - throw "DW_EH_PE_textrel pointer encoding not supported"; - break; - case DW_EH_PE_datarel: - throw "DW_EH_PE_datarel pointer encoding not supported"; - break; - case DW_EH_PE_funcrel: - throw "DW_EH_PE_funcrel pointer encoding not supported"; - break; - case DW_EH_PE_aligned: - throw "DW_EH_PE_aligned pointer encoding not supported"; - break; - default: - throwf("ObjectFileAddressSpace<A>::getEncodedP() encoding 0x%08X not supported", encoding); - break; - } - - if ( encoding & DW_EH_PE_indirect ) - result = getP(result); - - return result; -} - -template <> -uint32_t SymbolAtom<x86>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86>, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); - } - return result; -} - -template <> -uint32_t SymbolAtom<x86_64>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86_64>, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s because %s", this->getDisplayName(), fOwner.getPath(), warningBuffer); - } - return result; -} - -template <> -uint32_t SymbolAtom<ppc>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc - return 0; -} - -template <> -uint32_t SymbolAtom<ppc64>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc64 - return 0; -} - -template <> -uint32_t SymbolAtom<arm>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for arm - return 0; -} - - -template <typename A> -uint8_t SymbolAtom<A>::getLSDAReferenceKind() const -{ - return A::kGroupSubordinate; -} - -template <> -uint8_t SymbolAtom<x86_64>::getPersonalityReferenceKind() const -{ - return x86_64::kGOTNoFixUp; -} - -template <> -uint8_t SymbolAtom<x86>::getPersonalityReferenceKind() const -{ - return x86::kNoFixUp; -} - -template <typename A> -uint8_t SymbolAtom<A>::getPersonalityReferenceKind() const -{ - // only used with architectures that support compact unwinding - return 0; -} - - -template <> -uint32_t AnonymousAtom<x86>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86>, libunwind::Registers_x86>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); - } - return result; -} - -template <> -uint32_t AnonymousAtom<x86_64>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - pint_t lsda; - pint_t personality; - char warningBuffer[1024]; - uint32_t result = libunwind::DwarfInstructions<class ObjectFileAddressSpace<x86_64>, libunwind::Registers_x86_64>::createCompactEncodingFromFDE( - fOwner.fObjectAddressSpace, ehAtomAddress, &lsda, &personality, warningBuffer); - if ( (result & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) { - //if ( fOwner.fOptions.fForDyld ) - // throwf("can't make compact unwind encoding from dwarf for %s", this->getDisplayName()); - //else - if ( fOwner.fOptions.fWarnCompactUnwind ) - warning("can't make compact unwind encoding from dwarf for %s in %s", this->getDisplayName(), fOwner.getPath()); - } - return result; -} - -template <> -uint32_t AnonymousAtom<ppc>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc - return 0; -} - -template <> -uint32_t AnonymousAtom<ppc64>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for ppc64 - return 0; -} - -template <> -uint32_t AnonymousAtom<arm>::getCompactUnwindEncoding(uint64_t ehAtomAddress) -{ - // compact encoding not supported for arm - return 0; -} - - -template <typename A> -uint8_t AnonymousAtom<A>::getLSDAReferenceKind() const -{ - return A::kGroupSubordinate; -} - -template <> -uint8_t AnonymousAtom<x86_64>::getPersonalityReferenceKind() const -{ - return x86_64::kGOTNoFixUp; -} - -template <> -uint8_t AnonymousAtom<x86>::getPersonalityReferenceKind() const -{ - return x86::kNoFixUp; -} - -template <typename A> -uint8_t AnonymousAtom<A>::getPersonalityReferenceKind() const -{ - // only used with architectures that support compact unwinding - return 0; -} - - - - - - - -template <> -void Reader<ppc>::setCpuConstraint(uint32_t cpusubtype) -{ - switch (cpusubtype) { - case CPU_SUBTYPE_POWERPC_ALL: - case CPU_SUBTYPE_POWERPC_750: - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - case CPU_SUBTYPE_POWERPC_970: - fCpuConstraint = cpusubtype; - break; - default: - warning("unknown ppc subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); - fCpuConstraint = CPU_SUBTYPE_POWERPC_ALL; - break; - } -} - -template <> -void Reader<arm>::setCpuConstraint(uint32_t cpusubtype) -{ - switch (cpusubtype) { - case CPU_SUBTYPE_ARM_ALL: - case CPU_SUBTYPE_ARM_V4T: - case CPU_SUBTYPE_ARM_V5TEJ: - case CPU_SUBTYPE_ARM_V6: - case CPU_SUBTYPE_ARM_XSCALE: - case CPU_SUBTYPE_ARM_V7: - fCpuConstraint = cpusubtype; - break; - default: - warning("unknown arm subtype 0x%08X in %s, defaulting to ALL", cpusubtype, fPath); - fCpuConstraint = CPU_SUBTYPE_ARM_ALL; - break; - } -} - -template <typename A> -void Reader<A>::setCpuConstraint(uint32_t cpusubtype) -{ - // no cpu sub types for this architecture -} - -template <> -uint32_t Reader<ppc>::updateCpuConstraint(uint32_t previous) -{ - switch ( previous ) { - case CPU_SUBTYPE_POWERPC_ALL: - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_750: - if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_7400 || - fCpuConstraint == CPU_SUBTYPE_POWERPC_7450 || - fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_7400: - case CPU_SUBTYPE_POWERPC_7450: - if ( fCpuConstraint == CPU_SUBTYPE_POWERPC_970 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_POWERPC_970: - // G5 can run everything - break; - default: - throw "Unhandled PPC cpu subtype!"; - break; - } - return previous; -} - - - -template <> -uint32_t Reader<arm>::updateCpuConstraint(uint32_t previous) -{ - switch (previous) { - case CPU_SUBTYPE_ARM_ALL: - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V5TEJ: - // v6, v7, and xscale are more constrained than previous file (v5), so use it - if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V6) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V7) - || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V4T: - // v5, v6, v7, and xscale are more constrained than previous file (v4t), so use it - if ( (fCpuConstraint == CPU_SUBTYPE_ARM_V7) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V6) - || (fCpuConstraint == CPU_SUBTYPE_ARM_V5TEJ) - || (fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE) ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_V6: - // v6 can run everything except xscale and v7 - if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) - throw "can't mix xscale and v6 code"; - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) - return fCpuConstraint; - break; - case CPU_SUBTYPE_ARM_XSCALE: - // xscale can run everything except v6 and v7 - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V6 ) - throw "can't mix xscale and v6 code"; - if ( fCpuConstraint == CPU_SUBTYPE_ARM_V7 ) - throw "can't mix xscale and v7 code"; - break; - case CPU_SUBTYPE_ARM_V7: - // v7 can run everything except xscale - if ( fCpuConstraint == CPU_SUBTYPE_ARM_XSCALE ) - throw "can't mix xscale and v7 code"; - break; - default: - throw "Unhandled ARM cpu subtype!"; - } - return previous; -} - -template <typename A> -uint32_t Reader<A>::updateCpuConstraint(uint32_t current) -{ - // no cpu sub types for this architecture - return current; -} - -template <typename A> -void Reader<A>::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<const char*>::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<x86_64>::validSectionType(uint8_t type) -{ - switch ( type ) { - case S_SYMBOL_STUBS: - throw "symbol_stub sections not valid in x86_64 object files"; - case S_LAZY_SYMBOL_POINTERS: - throw "lazy pointer sections not valid in x86_64 object files"; - case S_NON_LAZY_SYMBOL_POINTERS: - throw "non lazy pointer sections not valid in x86_64 object files"; - } -} - -template <typename A> -void Reader<A>::validSectionType(uint8_t type) -{ -} - -template <typename A> -bool Reader<A>::getTranslationUnitSource(const char** dir, const char** name) const -{ - if ( fDebugInfo == kDebugInfoDwarf ) { - *dir = fDwarfTranslationUnitDir; - *name = fDwarfTranslationUnitFile; - return (fDwarfTranslationUnitFile != NULL); - } - return false; -} - -template <typename A> -BaseAtom* Reader<A>::findAtomByName(const char* name) -{ - // first search the more important atoms - for (typename AddrToAtomMap::iterator it=fAddrToAtom.begin(); it != fAddrToAtom.end(); it++) { - const char* atomName = it->second->getName(); - if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { - return it->second; - } - } - // try all atoms, because this might have been a tentative definition - for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); it++) { - BaseAtom* atom = (BaseAtom*)(*it); - const char* atomName = atom->getName(); - if ( (atomName != NULL) && (strcmp(atomName, name) == 0) ) { - return atom; - } - } - return NULL; -} - -template <typename A> -Reference<A>* Reader<A>::makeReference(Kinds kind, pint_t atAddr, pint_t toAddr) -{ - return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toAddr)); -} - -template <typename A> -Reference<A>* Reader<A>::makeReference(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr) -{ - return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toAddr)); -} - -template <typename A> -Reference<A>* Reader<A>::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t toAddr, pint_t toBaseAddr) -{ - return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(toBaseAddr, toAddr)); -} - -template <typename A> -Reference<A>* Reader<A>::makeReferenceWithToBase(Kinds kind, pint_t atAddr, pint_t fromAddr, pint_t toAddr, pint_t toBaseAddr) -{ - return new Reference<A>(kind, findAtomAndOffset(atAddr), findAtomAndOffset(fromAddr), findAtomAndOffset(toBaseAddr, toAddr)); -} - -template <typename A> -Reference<A>* Reader<A>::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) -{ - return new Reference<A>(kind, findAtomAndOffset(atAddr), toName, toOffset); -} - -template <typename A> -BaseAtom* Reader<A>::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect) -{ - // add a group subordinate reference from function atom to its eh frame atom - const uint8_t* ehContent = (const uint8_t*)(fHeader) + ehAtomAddress - ehSect->addr() + ehSect->offset(); - int32_t deltaMinus8 = P::getP(*(pint_t*)(&ehContent[8])); // offset 8 in eh info is delta to function - pint_t funcAddr = ehAtomAddress + deltaMinus8 + 8; - ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom; - ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom; - new Reference<A>(A::kGroupSubordinate, funcAtom, ehAtom); - return (BaseAtom*)funcAtom; -} - - -template <> -Reference<x86_64>* Reader<x86_64>::makeByNameReference(Kinds kind, pint_t atAddr, const char* toName, uint32_t toOffset) -{ - // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references - // instead check scope of target - BaseAtom* target = findAtomByName(toName); - if ( (target != NULL) && (target->getScope() == ObjectFile::Atom::scopeTranslationUnit) ) - return new Reference<x86_64>(kind, findAtomAndOffset(atAddr), AtomAndOffset(target, toOffset)); - else - return new Reference<x86_64>(kind, findAtomAndOffset(atAddr), toName, toOffset); -} - -template <> -Reference<x86_64>* Reader<x86_64>::makeReferenceToSymbol(Kinds kind, pint_t atAddr, const macho_nlist<P>* toSymbol, pint_t toOffset) -{ - // x86_64 uses external relocations everywhere, so external relocations do not imply by-name references - // instead check scope of target - const char* symbolName = &fStrings[toSymbol->n_strx()]; - if ( ((toSymbol->n_type() & N_TYPE) == N_SECT) && (((toSymbol->n_type() & N_EXT) == 0) || (symbolName[0] == 'L')) ) { - AtomAndOffset targetAO = findAtomAndOffsetForSection(toSymbol->n_value(), toSymbol->n_sect()); - targetAO.offset = toOffset; - return new Reference<x86_64>(kind, findAtomAndOffset(atAddr), targetAO); - } - else - return new Reference<x86_64>(kind, findAtomAndOffset(atAddr), symbolName, toOffset); -} - - -template <> -BaseAtom* Reader<x86_64>::makeReferenceToEH(const char* ehName, pint_t ehAtomAddress, const macho_section<P>* ehSect) -{ - // add a group subordinate reference from function atom to its eh frame atom - // for x86_64 the __eh_frame section contains the addends, so need to use relocs to find target - uint32_t ehAtomDeltaSectionOffset = ehAtomAddress + 8 - ehSect->addr(); // offset 8 in eh info is delta to function - const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + ehSect->reloff()); - const macho_relocation_info<P>* relocsEnd = &relocs[ehSect->nreloc()]; - for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) { - if ( (reloc->r_address() == ehAtomDeltaSectionOffset) && (reloc->r_type() == X86_64_RELOC_UNSIGNED) ) { - pint_t funcAddr = fSymbols[reloc->r_symbolnum()].n_value(); - ObjectFile::Atom* funcAtom = findAtomAndOffset(funcAddr).atom; - ObjectFile::Atom* ehAtom = findAtomAndOffset(ehAtomAddress).atom; - new Reference<x86_64>(x86_64::kGroupSubordinate, funcAtom, ehAtom); - return (BaseAtom*)funcAtom; - } - } - warning("can't find matching function for eh symbol %s", ehName); - return NULL; -} - -template <typename A> -AtomAndOffset Reader<A>::findAtomAndOffsetForSection(pint_t addr, unsigned int expectedSectionIndex) -{ - AtomAndOffset ao = findAtomAndOffset(addr); - if ( ao.atom != NULL ) { - if ( ((BaseAtom*)(ao.atom))->getSectionIndex() == expectedSectionIndex ) - return ao; - } - // The atom found is not in the section expected. - // This probably means there was a label at the end of the section. - // Do a slow sequential lookup - for (std::vector<BaseAtom*>::iterator it=fAtoms.begin(); it != fAtoms.end(); ++it) { - BaseAtom* atom = *it; - if ( atom->getSectionIndex() == expectedSectionIndex ) { - pint_t objAddr = atom->getObjectAddress(); - if ( (objAddr == addr) || ((objAddr < addr) && (objAddr+atom->getSize() > addr)) ) { - return AtomAndOffset(atom, addr-atom->getObjectAddress()); - } - } - } - // no atom found that matched section, fall back to one orginally found - return ao; -} - -template <typename A> -AtomAndOffset Reader<A>::findAtomAndOffset(pint_t addr) -{ - // STL has no built-in for "find largest key that is same or less than" - typename AddrToAtomMap::iterator it = fAddrToAtom.upper_bound(addr); - // if no atoms up to this address return none found - if ( it == fAddrToAtom.begin() ) - return AtomAndOffset(NULL); - // otherwise upper_bound gets us next key, so we back up one - --it; - AtomAndOffset result; - result.atom = it->second; - result.offset = addr - it->first; - //fprintf(stderr, "findAtomAndOffset(0x%0llX) ==> %s (0x%0llX -> 0x%0llX)\n", - // (uint64_t)addr, result.atom->getDisplayName(), (uint64_t)it->first, it->first+result.atom->getSize()); - return result; -} - -// "scattered" relocations enable you to offset into an atom past the end of it -// baseAddr is the address of the target atom, -// realAddr is the points into it -template <typename A> -AtomAndOffset Reader<A>::findAtomAndOffset(pint_t baseAddr, pint_t realAddr) -{ - typename AddrToAtomMap::iterator it = fAddrToAtom.find(baseAddr); - if ( it != fAddrToAtom.end() ) { - AtomAndOffset result; - result.atom = it->second; - result.offset = realAddr - it->first; - if ( result.atom->isThumb() ) - result.offset &= -2; - //fprintf(stderr, "findAtomAndOffset(0x%08X, 0x%08X) => %s + 0x%08X\n", baseAddr, realAddr, result.atom->getDisplayName(), result.offset); - return result; - } - // getting here means we have a scattered relocation to an address without a label - // so, find the atom that contains the baseAddr, and offset from that to the readAddr - AtomAndOffset result = findAtomAndOffset(baseAddr); - result.offset += (realAddr-baseAddr); - return result; -} - - -/* Skip over a LEB128 value (signed or unsigned). */ -static void -skip_leb128 (const uint8_t ** offset, const uint8_t * end) -{ - while (*offset != end && **offset >= 0x80) - (*offset)++; - if (*offset != end) - (*offset)++; -} - -/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow - or error. On overflow, skip past the rest of the uleb128. */ -static uint64_t -read_uleb128 (const uint8_t ** offset, const uint8_t * end) -{ - uint64_t result = 0; - int bit = 0; - - do { - uint64_t b; - - if (*offset == end) - return (uint64_t) -1; - - b = **offset & 0x7f; - - if (bit >= 64 || b << bit >> bit != b) - result = (uint64_t) -1; - else - result |= b << bit, bit += 7; - } while (*(*offset)++ >= 0x80); - return result; -} - - -/* Skip over a DWARF attribute of form FORM. */ -template <typename A> -bool Reader<A>::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, - uint8_t addr_size, bool dwarf64) -{ - int64_t sz=0; - - switch (form) - { - case DW_FORM_addr: - sz = addr_size; - break; - - case DW_FORM_block2: - if (end - *offset < 2) - return false; - sz = 2 + A::P::E::get16(*(uint16_t*)offset); - break; - - case DW_FORM_block4: - if (end - *offset < 4) - return false; - sz = 2 + A::P::E::get32(*(uint32_t*)offset); - break; - - case DW_FORM_data2: - case DW_FORM_ref2: - sz = 2; - break; - - case DW_FORM_data4: - case DW_FORM_ref4: - sz = 4; - break; - - case DW_FORM_data8: - case DW_FORM_ref8: - sz = 8; - break; - - case DW_FORM_string: - while (*offset != end && **offset) - ++*offset; - case DW_FORM_data1: - case DW_FORM_flag: - case DW_FORM_ref1: - sz = 1; - break; - - case DW_FORM_block: - sz = read_uleb128 (offset, end); - break; - - case DW_FORM_block1: - if (*offset == end) - return false; - sz = 1 + **offset; - break; - - case DW_FORM_sdata: - case DW_FORM_udata: - case DW_FORM_ref_udata: - skip_leb128 (offset, end); - return true; - - case DW_FORM_strp: - case DW_FORM_ref_addr: - sz = 4; - break; - - default: - return false; - } - if (end - *offset < sz) - return false; - *offset += sz; - return true; -} - -template <typename A> -const char* Reader<A>::getDwarfString(uint64_t form, const uint8_t* p) -{ - if ( form == DW_FORM_string ) - return (const char*)p; - else if ( form == DW_FORM_strp ) { - uint32_t offset = E::get32(*((uint32_t*)p)); - const char* dwarfStrings = (char*)(fHeader) + fDwarfDebugStringSect->offset(); - if ( offset > fDwarfDebugStringSect->size() ) { - warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->getPath()); - return NULL; - } - return &dwarfStrings[offset]; - } - warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->getPath()); - return NULL; -} - - -// Look at the compilation unit DIE and determine -// its NAME, compilation directory (in COMP_DIR) and its -// line number information offset (in STMT_LIST). NAME and COMP_DIR -// may be NULL (especially COMP_DIR) if they are not in the .o file; -// STMT_LIST will be (uint64_t) -1. -// -// At present this assumes that there's only one compilation unit DIE. -// -template <typename A> -bool Reader<A>::read_comp_unit(const char ** name, const char ** comp_dir, - uint64_t *stmt_list) -{ - const uint8_t * debug_info; - const uint8_t * debug_abbrev; - const uint8_t * di; - const uint8_t * da; - const uint8_t * end; - const uint8_t * enda; - uint64_t sz; - uint16_t vers; - uint64_t abbrev_base; - uint64_t abbrev; - uint8_t address_size; - bool dwarf64; - - *name = NULL; - *comp_dir = NULL; - *stmt_list = (uint64_t) -1; - - if ( (fDwarfDebugInfoSect == NULL) || (fDwarfDebugAbbrevSect == NULL) ) - return false; - - debug_info = (uint8_t*)(fHeader) + fDwarfDebugInfoSect->offset(); - debug_abbrev = (uint8_t*)(fHeader) + fDwarfDebugAbbrevSect->offset(); - di = debug_info; - - if (fDwarfDebugInfoSect->size() < 12) - /* Too small to be a real debug_info section. */ - return false; - sz = A::P::E::get32(*(uint32_t*)di); - di += 4; - dwarf64 = sz == 0xffffffff; - if (dwarf64) - sz = A::P::E::get64(*(uint64_t*)di), di += 8; - else if (sz > 0xffffff00) - /* Unknown dwarf format. */ - return false; - - /* Verify claimed size. */ - if (sz + (di - debug_info) > fDwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) - return false; - - vers = A::P::E::get16(*(uint16_t*)di); - if (vers < 2 || vers > 3) - /* DWARF version wrong for this code. - Chances are we could continue anyway, but we don't know for sure. */ - return false; - di += 2; - - /* Find the debug_abbrev section. */ - abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); - di += dwarf64 ? 8 : 4; - - if (abbrev_base > fDwarfDebugAbbrevSect->size()) - return false; - da = debug_abbrev + abbrev_base; - enda = debug_abbrev + fDwarfDebugAbbrevSect->size(); - - address_size = *di++; - - /* Find the abbrev number we're looking for. */ - end = di + sz; - abbrev = read_uleb128 (&di, end); - if (abbrev == (uint64_t) -1) - return false; - - /* Skip through the debug_abbrev section looking for that abbrev. */ - for (;;) - { - uint64_t this_abbrev = read_uleb128 (&da, enda); - uint64_t attr; - - if (this_abbrev == abbrev) - /* This is almost always taken. */ - break; - skip_leb128 (&da, enda); /* Skip the tag. */ - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - do { - attr = read_uleb128 (&da, enda); - skip_leb128 (&da, enda); - } while (attr != 0 && attr != (uint64_t) -1); - if (attr != 0) - return false; - } - - /* Check that the abbrev is one for a DW_TAG_compile_unit. */ - if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) - return false; - if (da == enda) - return false; - da++; /* Skip the DW_CHILDREN_* value. */ - - /* Now, go through the DIE looking for DW_AT_name, - DW_AT_comp_dir, and DW_AT_stmt_list. */ - for (;;) - { - uint64_t attr = read_uleb128 (&da, enda); - uint64_t form = read_uleb128 (&da, enda); - - if (attr == (uint64_t) -1) - return false; - else if (attr == 0) - return true; - - if (form == DW_FORM_indirect) - form = read_uleb128 (&di, end); - - if (attr == DW_AT_name) - *name = getDwarfString(form, di); - else if (attr == DW_AT_comp_dir) - *comp_dir = getDwarfString(form, di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) - *stmt_list = A::P::E::get32(*(uint32_t*)di); - else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) - *stmt_list = A::P::E::get64(*(uint64_t*)di); - if (! skip_form (&di, end, form, address_size, dwarf64)) - return false; - } -} - -template <typename A> -const char* Reader<A>::assureFullPath(const char* path) -{ - if ( path[0] == '/' ) - return path; - char cwdbuff[MAXPATHLEN]; - if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { - char* result; - asprintf(&result, "%s/%s", cwdbuff, path); - if ( result != NULL ) - return result; - } - return path; -} - - -// -// -// To implement architecture xxx, you must write template specializations for the following six methods: -// Reader<xxx>::validFile() -// Reader<xxx>::addRelocReference() -// Reference<xxx>::getDescription() -// -// - - -template <> -bool Reader<ppc>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader<ppc64>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader<x86>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_I386 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader<x86_64>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC_64 ) - return false; - if ( header->cputype() != CPU_TYPE_X86_64 ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - return true; -} - -template <> -bool Reader<arm>::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return false; - if ( header->cputype() != CPU_TYPE_ARM ) - return false; - if ( header->filetype() != MH_OBJECT ) - return false; - if ( subtypeMustMatch && ((cpu_subtype_t)header->cpusubtype() != subtype) ) - return false; - return true; -} - - -template <> -const char* Reader<ppc>::fileKind(const uint8_t* fileContent) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_POWERPC ) - return NULL; - switch ( header->cpusubtype() ) { - case CPU_SUBTYPE_POWERPC_750: - return "ppc750"; - case CPU_SUBTYPE_POWERPC_7400: - return "ppc7400"; - case CPU_SUBTYPE_POWERPC_7450: - return "ppc7450"; - case CPU_SUBTYPE_POWERPC_970: - return "ppc970"; - case CPU_SUBTYPE_POWERPC_ALL: - return "ppc"; - } - return "ppc???"; -} - -template <> -const char* Reader<ppc64>::fileKind(const uint8_t* fileContent) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_POWERPC64 ) - return NULL; - return "ppc64"; -} - -template <> -const char* Reader<x86>::fileKind(const uint8_t* fileContent) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_I386 ) - return NULL; - return "i386"; -} - -template <> -const char* Reader<x86_64>::fileKind(const uint8_t* fileContent) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_X86_64 ) - return NULL; - return "x86_64"; -} - -template <> -const char* Reader<arm>::fileKind(const uint8_t* fileContent) -{ - const macho_header<P>* header = (const macho_header<P>*)fileContent; - if ( header->magic() != MH_MAGIC ) - return NULL; - if ( header->cputype() != CPU_TYPE_ARM ) - return NULL; - switch ( header->cpusubtype() ) { - case CPU_SUBTYPE_ARM_V4T: - return "armv4t"; - case CPU_SUBTYPE_ARM_V5TEJ: - return "armv5"; - case CPU_SUBTYPE_ARM_V6: - return "armv6"; - case CPU_SUBTYPE_ARM_V7: - return "armv7"; - } - return "arm???"; -} - - -template <typename A> -bool Reader<A>::isWeakImportSymbol(const macho_nlist<P>* sym) -{ - return ( ((sym->n_type() & N_TYPE) == N_UNDF) && ((sym->n_desc() & N_WEAK_REF) != 0) ); -} - -template <> -bool Reader<ppc64>::addRelocReference(const macho_section<ppc64::P>* sect, const macho_relocation_info<ppc64::P>* reloc) -{ - return addRelocReference_powerpc(sect, reloc); -} - -template <> -bool Reader<ppc>::addRelocReference(const macho_section<ppc::P>* sect, const macho_relocation_info<ppc::P>* reloc) -{ - return addRelocReference_powerpc(sect, reloc); -} - - -// -// ppc and ppc64 both use the same relocations, so process them in one common routine -// -template <typename A> -bool Reader<A>::addRelocReference_powerpc(const macho_section<typename A::P>* sect, - const macho_relocation_info<typename A::P>* reloc) -{ - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t* fixUpPtr; - int32_t displacement = 0; - uint32_t instruction = 0; - uint32_t offsetInTarget; - int16_t lowBits; - bool result = false; - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - const macho_relocation_info<P>* nextReloc = &reloc[1]; - const char* targetName = NULL; - bool weakImport = false; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - if ( reloc->r_type() != PPC_RELOC_PAIR ) - instruction = BigEndian::get32(*fixUpPtr); - srcAddr = sect->addr() + reloc->r_address(); - if ( reloc->r_extern() ) { - const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - weakImport = this->isWeakImportSymbol(targetSymbol); - } - switch ( reloc->r_type() ) { - case PPC_RELOC_BR24: - { - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - } - else { - printf("bad instruction for BR24 reloc"); - } - if ( reloc->r_extern() ) { - offsetInTarget = srcAddr + displacement; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(A::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(A::kBranch24, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - // if this is a branch to a stub, we need to see if the stub is for a weak imported symbol - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(A::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(A::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom<A>*)atom)->isWeakImportStub() ) - makeReference(A::kBranch24WeakImport, srcAddr, dstAddr); - else - makeReference(A::kBranch24, srcAddr, dstAddr); - } - } - break; - case PPC_RELOC_BR14: - { - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - if ( reloc->r_extern() ) { - offsetInTarget = srcAddr + displacement; - makeByNameReference(A::kBranch14, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - makeReference(A::kBranch14, srcAddr, dstAddr); - } - } - break; - case PPC_RELOC_PAIR: - // skip, processed by a previous look ahead - break; - case PPC_RELOC_LO16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_LO16 missing following pair"; - } - result = true; - lowBits = (instruction & 0xFFFF); - if ( reloc->r_extern() ) { - offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeByNameReference(A::kAbsLow16, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsLow16, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsLow16, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsLow16, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_LO14: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_LO14 missing following pair"; - } - result = true; - lowBits = (instruction & 0xFFFC); - if ( reloc->r_extern() ) { - offsetInTarget = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeByNameReference(A::kAbsLow14, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = (nextReloc->r_address() << 16) | ((uint32_t)lowBits & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsLow14, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsLow14, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsLow14, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_HI16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_HI16 missing following pair"; - } - result = true; - if ( reloc->r_extern() ) { - offsetInTarget = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - makeByNameReference(A::kAbsHigh16, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = ((instruction & 0x0000FFFF) << 16) | (nextReloc->r_address() & 0x0000FFFF); - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsHigh16, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsHigh16, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsHigh16, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_HA16: - { - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_HA16 missing following pair"; - } - result = true; - lowBits = (nextReloc->r_address() & 0x0000FFFF); - if ( reloc->r_extern() ) { - offsetInTarget = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - makeByNameReference(A::kAbsHigh16AddLow, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - if ( reloc->r_symbolnum() == R_ABS ) { - // find absolute symbol that corresponds to pointerValue - typename AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(A::kAbsHigh16AddLow, srcAddr, pos->second->getName(), 0); - else - makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); - } - else { - makeReference(A::kAbsHigh16AddLow, srcAddr, dstAddr); - } - } - } - break; - case PPC_RELOC_VANILLA: - { - pint_t pointerValue = P::getP(*((pint_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - if ( weakImport ) - makeByNameReference(A::kPointerWeakImport, srcAddr, targetName, pointerValue); - else - makeByNameReference(A::kPointer, srcAddr, targetName, pointerValue); - } - else { - new Reference<A>(A::kPointer, findAtomAndOffset(srcAddr), findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum())); - } - } - break; - case PPC_RELOC_JBSR: - // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target - if ( nextReloc->r_type() != PPC_RELOC_PAIR ) { - throw "PPC_RELOC_JBSR missing following pair"; - } - if ( !fHasLongBranchStubs ) - warning("object file compiled with -mlong-branch which is no longer needed. To remove this warning, recompile without -mlong-branch: %s", fPath); - fHasLongBranchStubs = true; - result = true; - if ( reloc->r_extern() ) { - throw "PPC_RELOC_JBSR should not be using an external relocation"; - } - makeReference(A::kBranch24, srcAddr, nextReloc->r_address()); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - } - else { - fprintf(stderr, "bad instruction for BR24 reloc"); - } - break; - default: - warning("unknown relocation type %d", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - uint32_t betterDstAddr; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1]; - const macho_relocation_info<P>* nextReloc = &reloc[1]; - // file format allows pair to be scattered or not - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - result = true; - } - } - else { - if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - result = true; - } - } - switch (sreloc->r_type()) { - case PPC_RELOC_VANILLA: - { - betterDstAddr = P::getP(*(pint_t*)fixUpPtr); - //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - makeReferenceWithToBase(A::kPointer, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_BR14: - { - instruction = BigEndian::get32(*fixUpPtr); - displacement = (instruction & 0x0000FFFC); - if ( (displacement & 0x00008000) != 0 ) - displacement |= 0xFFFF0000; - betterDstAddr = srcAddr+displacement; - //fprintf(stderr, "betterDstAddr=0x%08X, srcAddr=0x%08X, displacement=0x%08X\n", betterDstAddr, srcAddr, displacement); - makeReferenceWithToBase(A::kBranch14, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_BR24: - { - instruction = BigEndian::get32(*fixUpPtr); - if ( (instruction & 0x4C000000) == 0x48000000 ) { - displacement = (instruction & 0x03FFFFFC); - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - betterDstAddr = srcAddr+displacement; - makeReferenceWithToBase(A::kBranch24, srcAddr, betterDstAddr, dstAddr); - } - } - break; - case PPC_RELOC_LO16_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO16_SECTDIFF missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFF); - displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kPICBaseLow16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_LO14_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO14_SECTDIFF missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFC); - displacement = (nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kPICBaseLow14, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_HA16_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_HA16_SECTDIFF missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0x0000FFFF); - displacement = ((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits; - makeReferenceWithToBase(A::kPICBaseHigh16, srcAddr, nextRelocValue, nextRelocValue + displacement, dstAddr); - } - break; - case PPC_RELOC_LO14: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO14 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFC); - betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsLow14, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_LO16: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_LO16 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (instruction & 0xFFFF); - betterDstAddr = (nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsLow16, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_HA16: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_HA16 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0xFFFF); - betterDstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; - makeReferenceWithToBase(A::kAbsHigh16AddLow, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_HI16: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_HI16 missing following pair"; - } - instruction = BigEndian::get32(*fixUpPtr); - lowBits = (nextRelocAddress & 0xFFFF); - betterDstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); - makeReferenceWithToBase(A::kAbsHigh16, srcAddr, betterDstAddr, dstAddr); - } - break; - case PPC_RELOC_SECTDIFF: - case PPC_RELOC_LOCAL_SECTDIFF: - { - if ( ! nextRelocIsPair ) { - throw "PPC_RELOC_SECTDIFF missing following pair"; - } - Kinds kind = A::kPointerDiff32;; - uint32_t contentAddr = 0; - switch ( sreloc->r_length() ) { - case 0: - throw "bad diff relocations r_length (0) for ppc architecture"; - case 1: - kind = A::kPointerDiff16; - contentAddr = BigEndian::get16(*((uint16_t*)fixUpPtr)); - break; - case 2: - kind = A::kPointerDiff32; - contentAddr = BigEndian::get32(*fixUpPtr); - break; - case 3: - kind = A::kPointerDiff64; - contentAddr = BigEndian::get64(*((uint64_t*)fixUpPtr)); - break; - } - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", - // dstAddr, nextRelocValue, contentAddr); - if ( (dstAddr - nextRelocValue) != contentAddr ) { - if ( toao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else - fromao.offset += (dstAddr - contentAddr) - nextRelocValue; - } - //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", - // srcao.atom->getDisplayName(), srcao.offset, - // fromao.atom->getDisplayName(), fromao.offset, - // toao.atom->getDisplayName(), toao.offset); - new Reference<A>(kind, srcao, fromao, toao); - } - break; - case PPC_RELOC_PAIR: - break; - case PPC_RELOC_HI16_SECTDIFF: - warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); - break; - default: - warning("unknown scattered relocation type %d", sreloc->r_type()); - } - } - return result; -} - - -template <> -bool Reader<x86>::addRelocReference(const macho_section<x86::P>* sect, const macho_relocation_info<x86::P>* reloc) -{ - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t* fixUpPtr; - bool result = false; - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - switch ( reloc->r_type() ) { - case GENERIC_RELOC_VANILLA: - { - x86::ReferenceKinds kind = x86::kPointer; - uint32_t pointerValue = E::get32(*fixUpPtr); - if ( reloc->r_pcrel() ) { - switch( reloc->r_length() ) { - case 0: - kind = x86::kPCRel8; - pointerValue = srcAddr + *((int8_t*)fixUpPtr) + sizeof(int8_t); - break; - case 1: - kind = x86::kPCRel16; - pointerValue = srcAddr + (int16_t)E::get16(*((uint16_t*)fixUpPtr)) + sizeof(uint16_t); - break; - case 2: - kind = x86::kPCRel32; - pointerValue += srcAddr + sizeof(uint32_t); - break; - case 3: - throw "bad pc-rel vanilla relocation length"; - } - } - else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { - kind = x86::kAbsolute32; - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - } - else { - kind = x86::kPointer; - if ( reloc->r_length() != 2 ) - throw "bad vanilla relocation length"; - } - if ( reloc->r_extern() ) { - const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - if ( this->isWeakImportSymbol(targetSymbol) ) { - if ( reloc->r_pcrel() ) - kind = x86::kPCRel32WeakImport; - else - kind = x86::kPointerWeakImport; - } - const char* targetName = &fStrings[targetSymbol->n_strx()]; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else - makeByNameReference(kind, srcAddr, targetName, pointerValue); - } - else { - AtomAndOffset targetAO = findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum()); - const char* targetName = targetAO.atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(x86::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(x86::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - // if this is a reference to a stub, we need to see if the stub is for a weak imported symbol - else if ( reloc->r_pcrel() && (targetAO.atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom<x86>*)targetAO.atom)->isWeakImportStub() ) - new Reference<x86>(x86::kPCRel32WeakImport, findAtomAndOffset(srcAddr), targetAO); - else if ( reloc->r_symbolnum() != R_ABS ) - new Reference<x86>(kind, findAtomAndOffset(srcAddr), targetAO); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(pointerValue); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(kind, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - } - break; - default: - warning("unknown relocation type %d", reloc->r_type()); - } - } - else { - const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1]; - const macho_relocation_info<P>* nextReloc = &reloc[1]; - pint_t betterDstAddr; - // file format allows pair to be scattered or not - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { - if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextReloc->r_address(); - result = true; - } - } - else { - if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - } - } - switch (sreloc->r_type()) { - case GENERIC_RELOC_VANILLA: - betterDstAddr = LittleEndian::get32(*fixUpPtr); - //fprintf(stderr, "pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08lX\n", srcAddr, dstAddr, betterDstAddr); - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - if ( sreloc->r_pcrel() ) { - switch ( sreloc->r_length() ) { - case 2: - betterDstAddr += srcAddr + 4; - makeReferenceWithToBase(x86::kPCRel32, srcAddr, betterDstAddr, dstAddr); - break; - case 1: - betterDstAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)) + srcAddr + 2; - makeReferenceWithToBase(x86::kPCRel16, srcAddr, betterDstAddr, dstAddr); - break; - case 0: - betterDstAddr = *((uint8_t*)fixUpPtr) + srcAddr + 1; - makeReferenceWithToBase(x86::kPCRel8, srcAddr, betterDstAddr, dstAddr); - break; - case 3: - throwf("unsupported r_length=3 for scattered pc-rel vanilla reloc"); - break; - } - } - else { - if ( sreloc->r_length() != 2 ) - throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length()); - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - makeReferenceWithToBase(x86::kAbsolute32, srcAddr, betterDstAddr, dstAddr); - else - makeReferenceWithToBase(x86::kPointer, srcAddr, betterDstAddr, dstAddr); - } - break; - case GENERIC_RELOC_SECTDIFF: - case GENERIC_RELOC_LOCAL_SECTDIFF: - { - if ( !nextRelocIsPair ) { - throw "GENERIC_RELOC_SECTDIFF missing following pair"; - } - x86::ReferenceKinds kind = x86::kPointerDiff; - uint32_t contentAddr = 0; - switch ( sreloc->r_length() ) { - case 0: - case 3: - throw "bad length for GENERIC_RELOC_SECTDIFF"; - case 1: - kind = x86::kPointerDiff16; - contentAddr = LittleEndian::get16(*((uint16_t*)fixUpPtr)); - break; - case 2: - kind = x86::kPointerDiff; - contentAddr = LittleEndian::get32(*fixUpPtr); - break; - } - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - //fprintf(stderr, "addRef: dstAddr=0x%X, nextRelocValue=0x%X, contentAddr=0x%X\n", - // dstAddr, nextRelocValue, contentAddr); - if ( (dstAddr - nextRelocValue) != contentAddr ) { - if ( toao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else if ( fromao.atom == srcao.atom ) - toao.offset += (contentAddr + nextRelocValue) - dstAddr; - else - fromao.offset += (dstAddr - contentAddr) - nextRelocValue; - } - //fprintf(stderr, "addRef: src=%s+0x%X, from=%s+0x%X, to=%s+0x%X\n", - // srcao.atom->getDisplayName(), srcao.offset, - // fromao.atom->getDisplayName(), fromao.offset, - // toao.atom->getDisplayName(), toao.offset); - new Reference<x86>(kind, srcao, fromao, toao); - } - break; - case GENERIC_RELOC_PAIR: - // do nothing, already used via a look ahead - break; - default: - warning("unknown scattered relocation type %d", sreloc->r_type()); - } - } - return result; -} - -template <> -bool Reader<x86_64>::addRelocReference(const macho_section<x86_64::P>* sect, const macho_relocation_info<x86_64::P>* reloc) -{ - uint64_t srcAddr; - uint64_t dstAddr = 0; - uint64_t addend; - uint32_t* fixUpPtr; - x86_64::ReferenceKinds kind = x86_64::kNoFixUp; - bool result = false; - const macho_nlist<P>* targetSymbol = NULL; - const char* targetName = NULL; - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - //fprintf(stderr, "addReloc type=%d, len=%d, address=0x%X\n", reloc->r_type(), reloc->r_length(), reloc->r_address()); - if ( reloc->r_extern() ) { - targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - } - switch ( reloc->r_type() ) { - case X86_64_RELOC_UNSIGNED: - if ( reloc->r_pcrel() ) - throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; - switch ( reloc->r_length() ) { - case 0: - case 1: - throw "length < 2 and X86_64_RELOC_UNSIGNED not supported"; - case 2: - kind = x86_64::kPointer32; - break; - case 3: - if ( reloc->r_extern() && isWeakImportSymbol(targetSymbol) ) - kind = x86_64::kPointerWeakImport; - else - kind = x86_64::kPointer; - break; - } - dstAddr = E::get64(*((uint64_t*)fixUpPtr)); - if ( reloc->r_extern() ) { - makeReferenceToSymbol(kind, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(kind, srcAddr, dstAddr); - // verify that dstAddr is in the section being targeted - int sectNum = reloc->r_symbolnum(); - const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>)); - const macho_section<P>* const targetSection = §ionsStart[sectNum-1]; - if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { - throwf("local relocation for address 0x%08llX in section %s does not target section %s", - srcAddr, sect->sectname(), targetSection->sectname()); - } - } - break; - case X86_64_RELOC_SIGNED: - case X86_64_RELOC_SIGNED_1: - case X86_64_RELOC_SIGNED_2: - case X86_64_RELOC_SIGNED_4: - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( reloc->r_extern() ) { - switch ( reloc->r_type() ) { - case X86_64_RELOC_SIGNED: - kind = x86_64::kPCRel32; - // begin support for old .o files before X86_64_RELOC_SIGNED_1 was created - if ( addend == (uint64_t)(-1) ) { - addend = 0; - kind = x86_64::kPCRel32_1; - } - else if ( addend == (uint64_t)(-2) ) { - addend = 0; - kind = x86_64::kPCRel32_2; - } - else if ( addend == (uint64_t)(-4) ) { - addend = 0; - kind = x86_64::kPCRel32_4; - } - break; - // end support for old .o files before X86_64_RELOC_SIGNED_1 was created - case X86_64_RELOC_SIGNED_1: - kind = x86_64::kPCRel32_1; - addend += 1; - break; - case X86_64_RELOC_SIGNED_2: - kind = x86_64::kPCRel32_2; - addend += 2; - break; - case X86_64_RELOC_SIGNED_4: - kind = x86_64::kPCRel32_4; - addend += 4; - break; - } - makeReferenceToSymbol(kind, srcAddr, targetSymbol, addend); - } - else { - uint64_t ripRelativeOffset = addend; - switch ( reloc->r_type() ) { - case X86_64_RELOC_SIGNED: - dstAddr = srcAddr + 4 + ripRelativeOffset; - kind = x86_64::kPCRel32; - break; - case X86_64_RELOC_SIGNED_1: - dstAddr = srcAddr + 5 + ripRelativeOffset; - kind = x86_64::kPCRel32_1; - break; - case X86_64_RELOC_SIGNED_2: - dstAddr = srcAddr + 6 + ripRelativeOffset; - kind = x86_64::kPCRel32_2; - break; - case X86_64_RELOC_SIGNED_4: - dstAddr = srcAddr + 8 + ripRelativeOffset; - kind = x86_64::kPCRel32_4; - break; - } - makeReference(kind, srcAddr, dstAddr); - // verify that dstAddr is in the section being targeted - int sectNum = reloc->r_symbolnum(); - const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)fSegment + sizeof(macho_segment_command<P>)); - const macho_section<P>* const targetSection = §ionsStart[sectNum-1]; - if ( (dstAddr < targetSection->addr()) || (dstAddr > (targetSection->addr()+targetSection->size())) ) { - throwf("local relocation for address 0x%08llX in section %s does not target section %s", - srcAddr, sect->sectname(), targetSection->sectname()); - } - } - break; - case X86_64_RELOC_BRANCH: - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_BRANCH not supported"; - if ( reloc->r_length() == 2 ) { - dstAddr = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( reloc->r_extern() ) { - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(x86_64::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(x86_64::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kBranchPCRel32WeakImport, srcAddr, targetSymbol, dstAddr); - else - makeReferenceToSymbol(x86_64::kBranchPCRel32, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(x86_64::kBranchPCRel32, srcAddr, srcAddr+4+dstAddr); - } - } - else if ( reloc->r_length() == 0 ) { - dstAddr = *((int8_t*)fixUpPtr); - if ( reloc->r_extern() ) { - makeReferenceToSymbol(x86_64::kBranchPCRel8, srcAddr, targetSymbol, dstAddr); - } - else { - makeReference(x86_64::kBranchPCRel8, srcAddr, srcAddr+1+dstAddr); - } - } - else { - throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length());; - } - break; - case X86_64_RELOC_GOT: - if ( ! reloc->r_extern() ) - throw "not extern and X86_64_RELOC_GOT not supported"; - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_GOT not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_GOT not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kPCRel32GOTWeakImport, srcAddr, targetSymbol, addend); - else - makeReferenceToSymbol(x86_64::kPCRel32GOT, srcAddr, targetSymbol, addend); - break; - case X86_64_RELOC_GOT_LOAD: - if ( ! reloc->r_extern() ) - throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; - if ( ! reloc->r_pcrel() ) - throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; - if ( reloc->r_length() != 2 ) - throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; - addend = (int64_t)((int32_t)(E::get32(*fixUpPtr))); - if ( isWeakImportSymbol(targetSymbol) ) - makeReferenceToSymbol(x86_64::kPCRel32GOTLoadWeakImport, srcAddr, targetSymbol, addend); - else - makeReferenceToSymbol(x86_64::kPCRel32GOTLoad, srcAddr, targetSymbol, addend); - break; - case X86_64_RELOC_SUBTRACTOR: - { - if ( reloc->r_pcrel() ) - throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; - if ( reloc->r_length() < 2 ) - throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; - if ( !reloc->r_extern() ) - throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; - const macho_relocation_info<x86_64::P>* nextReloc = &reloc[1]; - if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) - throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; - result = true; - if ( nextReloc->r_pcrel() ) - throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; - if ( nextReloc->r_length() != reloc->r_length() ) - throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; - Reference<x86_64>* ref; - bool negativeAddend; - if ( reloc->r_length() == 2 ) { - kind = x86_64::kPointerDiff32; - dstAddr = E::get32(*fixUpPtr); // addend is in content - negativeAddend = ((dstAddr & 0x80000000) != 0); - } - else { - kind = x86_64::kPointerDiff; - dstAddr = E::get64(*((uint64_t*)fixUpPtr)); // addend is in content - negativeAddend = ((dstAddr & 0x8000000000000000ULL) != 0); - } - AtomAndOffset inAtomAndOffset = this->findAtomAndOffset(srcAddr); - ObjectFile::Atom* inAtom = inAtomAndOffset.atom; - // create reference with "to" target - if ( nextReloc->r_extern() ) { - const macho_nlist<P>* targetSymbol = &fSymbols[nextReloc->r_symbolnum()]; - const char* targetName = &fStrings[targetSymbol->n_strx()]; - ref = makeReferenceToSymbol(kind, srcAddr, targetSymbol, 0); - // if "to" is in this atom, change by-name to a direct reference - if ( strcmp(targetName, inAtom->getName()) == 0 ) - ref->setTarget(*inAtom, 0); - } - else { - ref = makeReference(kind, srcAddr, dstAddr); - } - // add in "from" target - if ( reloc->r_extern() ) { - const macho_nlist<P>* targetFromSymbol = &fSymbols[reloc->r_symbolnum()]; - const char* fromTargetName = &fStrings[targetFromSymbol->n_strx()]; - if ( (targetFromSymbol->n_type() & N_EXT) == 0 ) { - // from target is translation unit scoped, so use a direct reference - ref->setFromTarget(*(findAtomAndOffset(targetSymbol->n_value()).atom)); - } - else if ( strcmp(fromTargetName, inAtom->getName()) == 0 ) { - // if "from" is in this atom, change by-name to a direct reference - ref->setFromTarget(*inAtom); - } - else { - // some non-static other atom - ref->setFromTargetName(fromTargetName); - } - } - else { - throw "X86_64_RELOC_SUBTRACTOR not supported with r_extern=0"; - } - // addend goes in from side iff negative - if ( negativeAddend ) - ref->setFromTargetOffset(-dstAddr); - else - ref->setToTargetOffset(dstAddr); - break; - } - default: - warning("unknown relocation type %d", reloc->r_type()); - } - return result; -} - - -/// Reader<arm>::addRelocReference - -/// turns arm relocation entries into references. Returns true if the next -/// relocation should be skipped, false otherwise. -template <> -bool Reader<arm>::addRelocReference(const macho_section<arm::P>* sect, - const macho_relocation_info<arm::P>* reloc) -{ - uint32_t * fixUpPtr; - int32_t displacement; - uint32_t instruction = 0; - bool result = false; - uint32_t srcAddr; - uint32_t dstAddr; - uint32_t pointerValue; - arm::ReferenceKinds kind = arm::kNoFixUp; - - if ( (reloc->r_address() & R_SCATTERED) == 0 ) { - // non-scattered relocation - const char* targetName = NULL; - bool weakImport = false; - - srcAddr = sect->addr() + reloc->r_address(); - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + reloc->r_address()); - if ( reloc->r_type() != ARM_RELOC_PAIR ) - instruction = LittleEndian::get32(*fixUpPtr); - - if ( reloc->r_extern() ) { - const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - targetName = &fStrings[targetSymbol->n_strx()]; - weakImport = this->isWeakImportSymbol(targetSymbol); - } - - switch ( reloc->r_type() ) { - case ARM_RELOC_BR24: - // Sign-extend displacement - displacement = (instruction & 0x00FFFFFF) << 2; - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - // The pc added will be +8 from the pc - displacement += 8; - // If this is BLX add H << 1 - if ((instruction & 0xFE000000) == 0xFA000000) - displacement += ((instruction & 0x01000000) >> 23); - - if ( reloc->r_extern() ) { - uint32_t offsetInTarget = srcAddr + displacement; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(arm::kBranch24WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(arm::kBranch24, srcAddr, targetName, offsetInTarget); - } - else { - dstAddr = srcAddr + displacement; - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - // check for dtrace probes and weak_import stubs - const char* targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() ) - makeReference(arm::kBranch24WeakImport, srcAddr, dstAddr); - else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(arm::kBranch24, srcAddr, dstAddr); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(arm::kBranch24, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - break; - - case ARM_THUMB_RELOC_BR22: - // thumb2 added two more bits to displacement, complicating the displacement decoding - { - uint32_t s = (instruction >> 10) & 0x1; - uint32_t j1 = (instruction >> 29) & 0x1; - uint32_t j2 = (instruction >> 27) & 0x1; - uint32_t imm10 = instruction & 0x3FF; - uint32_t imm11 = (instruction >> 16) & 0x7FF; - uint32_t i1 = (j1 == s); - uint32_t i2 = (j2 == s); - uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); - int32_t sdis = dis; - if ( s ) - sdis |= 0xFE000000; - displacement = sdis; - } - // The pc added will be +4 from the pc - displacement += 4; - // If the instruction was blx, force the low 2 bits to be clear - dstAddr = srcAddr + displacement; - if ((instruction & 0xF8000000) == 0xE8000000) - dstAddr &= 0xFFFFFFFC; - - if ( reloc->r_extern() ) { - uint32_t offsetInTarget = dstAddr; - if ( strncmp(targetName, "___dtrace_probe$", 16) == 0 ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( strncmp(targetName, "___dtrace_isenabled$", 20) == 0 ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( weakImport ) - makeByNameReference(arm::kThumbBranch22WeakImport, srcAddr, targetName, offsetInTarget); - else - makeByNameReference(arm::kThumbBranch22, srcAddr, targetName, offsetInTarget); - } - else { - ObjectFile::Atom* atom = findAtomAndOffset(dstAddr).atom; - // check for dtrace probes and weak_import stubs - const char* targetName = atom->getName(); - if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_probe$", 16) == 0) ) { - makeByNameReference(arm::kDtraceProbeSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[16]); - } - else if ( (targetName != NULL) && (strncmp(targetName, "___dtrace_isenabled$", 20) == 0) ) { - makeByNameReference(arm::kDtraceIsEnabledSite, srcAddr, targetName, 0); - addDtraceExtraInfos(srcAddr, &targetName[20]); - } - else if ( (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn) - && ((AnonymousAtom<x86>*)atom)->isWeakImportStub() ) - makeReference(arm::kThumbBranch22WeakImport, srcAddr, dstAddr); - else if ( reloc->r_symbolnum() != R_ABS ) - makeReference(arm::kThumbBranch22, srcAddr, dstAddr); - else { - // find absolute symbol that corresponds to pointerValue - AddrToAtomMap::iterator pos = fAddrToAbsoluteAtom.find(dstAddr); - if ( pos != fAddrToAbsoluteAtom.end() ) - makeByNameReference(arm::kThumbBranch22, srcAddr, pos->second->getName(), 0); - else - throwf("R_ABS reloc but no absolute symbol at target address"); - } - } - break; - - case ARM_RELOC_VANILLA: - if ( reloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_VANILLA"; - - pointerValue = instruction; - kind = arm::kPointer; - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - kind = arm::kReadOnlyPointer; - if ( weakImport ) - kind = arm::kPointerWeakImport; - if ( reloc->r_extern() ) { - const macho_nlist<P>* targetSymbol = &fSymbols[reloc->r_symbolnum()]; - if ( (targetSymbol->n_desc() & N_ARM_THUMB_DEF) && (pointerValue == 1) ) - pointerValue = 0; - makeByNameReference(kind, srcAddr, targetName, pointerValue); - } - else { - AtomAndOffset at = findAtomAndOffset(srcAddr); - AtomAndOffset to = findAtomAndOffsetForSection(pointerValue, reloc->r_symbolnum()); - if ( to.atom->isThumb() ) - to.offset &= -2; - new Reference<arm>(kind, at, to); - } - break; - - case ARM_THUMB_32BIT_BRANCH: - // ignore old unnecessary relocs - break; - - default: - warning("unexpected relocation type %u", reloc->r_type()); - break; - } - } - else { - const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; - const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1]; - int32_t addend; - srcAddr = sect->addr() + sreloc->r_address(); - dstAddr = sreloc->r_value(); - uint32_t betterDstAddr; - fixUpPtr = (uint32_t*)((char*)(fHeader) + sect->offset() + sreloc->r_address()); - instruction = LittleEndian::get32(*fixUpPtr); - - // A ARM_RELOC_PAIR only follows ARM_RELOC_{SECTDIFF,LOCAL_SECTDIFF} - // relocation types, and it is an error to see one otherwise. - bool nextRelocIsPair = false; - uint32_t nextRelocAddress = 0; - uint32_t nextRelocValue = 0; - if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) { - nextRelocIsPair = true; - nextRelocAddress = nextSReloc->r_address(); - nextRelocValue = nextSReloc->r_value(); - result = true; - } - - switch (sreloc->r_type()) { - case ARM_RELOC_VANILLA: - if ( sreloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_VANILLA"; - - //fprintf(stderr, "scattered pointer reloc: srcAddr=0x%08X, dstAddr=0x%08X, pointer=0x%08X\n", srcAddr, dstAddr, betterDstAddr); - betterDstAddr = LittleEndian::get32(*fixUpPtr); - kind = arm::kPointer; - if ( strcmp(sect->segname(), "__TEXT") == 0 ) - kind = arm::kReadOnlyPointer; - // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) - makeReferenceWithToBase(kind, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_RELOC_BR24: - // Sign-extend displacement - displacement = (instruction & 0x00FFFFFF) << 2; - if ( (displacement & 0x02000000) != 0 ) - displacement |= 0xFC000000; - // The pc added will be +8 from the pc - displacement += 8; - // If this is BLX add H << 1 - if ((instruction & 0xFE000000) == 0xFA000000) - displacement += ((instruction & 0x01000000) >> 23); - betterDstAddr = srcAddr+displacement; - makeReferenceWithToBase(arm::kBranch24, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_THUMB_RELOC_BR22: - // thumb2 added two more bits to displacement, complicating the displacement decoding - { - uint32_t s = (instruction >> 10) & 0x1; - uint32_t j1 = (instruction >> 29) & 0x1; - uint32_t j2 = (instruction >> 27) & 0x1; - uint32_t imm10 = instruction & 0x3FF; - uint32_t imm11 = (instruction >> 16) & 0x7FF; - uint32_t i1 = (j1 == s); - uint32_t i2 = (j2 == s); - uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); - int32_t sdis = dis; - if ( s ) - sdis |= 0xFE000000; - displacement = sdis; - } - // The pc added will be +4 from the pc - displacement += 4; - betterDstAddr = srcAddr+displacement; - // If the instruction was blx, force the low 2 bits to be clear - if ((instruction & 0xF8000000) == 0xE8000000) - betterDstAddr &= 0xFFFFFFFC; - makeReferenceWithToBase(arm::kThumbBranch22, srcAddr, betterDstAddr, dstAddr); - break; - - case ARM_RELOC_SECTDIFF: - case ARM_RELOC_LOCAL_SECTDIFF: - if ( !nextRelocIsPair ) { - throw "ARM_RELOC_SECTDIFF missing following pair"; - } - if ( sreloc->r_length() != 2 ) - throw "bad length for ARM_RELOC_SECTDIFF"; - { - AtomAndOffset srcao = findAtomAndOffset(srcAddr); - AtomAndOffset fromao = findAtomAndOffset(nextRelocValue); - AtomAndOffset toao = findAtomAndOffset(dstAddr); - // check for addend encoded in the section content - pointerValue = LittleEndian::get32(*fixUpPtr); - addend = pointerValue - (dstAddr - nextRelocValue); - if ( toao.atom->isThumb() && (addend & 1) ) - addend &= -2; // remove thumb bit - if ( (dstAddr - nextRelocValue) != pointerValue ) { - if ( fromao.atom == srcao.atom ) { - if ( ((const macho_section<P>*)(((BaseAtom*)(srcao.atom))->getSectionRecord()))->flags() & S_ATTR_PURE_INSTRUCTIONS ) { - int pcBaseOffset = srcao.atom->isThumb() ? 4 : 8; - if ( addend == -pcBaseOffset ) { - fromao.offset -= addend; - } - else { - toao.offset += addend; - } - } - else { - toao.offset += addend; - } - } - else if ( toao.atom == srcao.atom ) - toao.offset += addend; - else - fromao.offset -= addend; - } - new Reference<arm>(arm::kPointerDiff, srcao, fromao, toao); - } - break; - - default: - warning("unexpected srelocation type %u", sreloc->r_type()); - break; - } - } - return result; -} - -template <typename A> -void Reader<A>::addReferencesForSection(const macho_section<P>* sect) -{ - // ignore dwarf sections. If ld ever supports processing dwarf, this logic will need to change - if ( (sect->flags() & S_ATTR_DEBUG) == 0 ) { - switch ( sect->flags() & SECTION_TYPE ) { - case S_SYMBOL_STUBS: - case S_LAZY_SYMBOL_POINTERS: - // we ignore compiler generated stubs, so ignore those relocs too - break; - default: - // ignore all relocations in __eh_frame section - if ( sect == fehFrameSection ) - return; - const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((char*)(fHeader) + sect->reloff()); - const uint32_t relocCount = sect->nreloc(); - //fprintf(stderr, "relocCount = %d in section %s\n", relocCount, sect->sectname()); - for (uint32_t r = 0; r < relocCount; ++r) { - try { - if ( addRelocReference(sect, &relocs[r]) ) - ++r; // skip next - } - catch (const char* msg) { - throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); - } - } - } - } -} - - -template <> -const char* Reference<x86>::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case x86::kNoFixUp: - sprintf(temp, "reference to "); - break; - case x86::kFollowOn: - sprintf(temp, "followed by "); - break; - case x86::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case x86::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case x86::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case x86::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86::kPCRel32WeakImport: - sprintf(temp, "offset 0x%04X, rel32 reference to weak imported ", fFixUpOffsetInSrc); - break; - case x86::kPCRel32: - sprintf(temp, "offset 0x%04X, rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86::kPCRel16: - sprintf(temp, "offset 0x%04X, rel16 reference to ", fFixUpOffsetInSrc); - break; - case x86::kPCRel8: - sprintf(temp, "offset 0x%04X, rel8 reference to ", fFixUpOffsetInSrc); - break; - case x86::kAbsolute32: - sprintf(temp, "offset 0x%04X, absolute32 reference to ", fFixUpOffsetInSrc); - break; - case x86::kImageOffset32: - sprintf(temp, "offset 0x%04X, 32-bit offset of ", fFixUpOffsetInSrc); - break; - case x86::kPointerDiff24: - sprintf(temp, "offset 0x%04X, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", - fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset, - this->getFromTargetDisplayName(), fFromTarget.offset ); - return temp; - break; - case x86::kSectionOffset24: - sprintf(temp, "offset 0x%04X, 24-bit section offset of ", fFixUpOffsetInSrc); - break; - case x86::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case x86::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case x86::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case x86::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - - -template <> -const char* Reference<ppc>::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case ppc::kNoFixUp: - sprintf(temp, "reference to "); - break; - case ppc::kFollowOn: - sprintf(temp, "followed by "); - break; - case ppc::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case ppc::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case ppc::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case ppc::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 16-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc::kPointerDiff32: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc::kPointerDiff64: - throw "unsupported refrence kind"; - break; - case ppc::kBranch24WeakImport: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case ppc::kBranch24: - case ppc::kBranch14: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case ppc::kPICBaseLow16: - sprintf(temp, "offset 0x%04X, low 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kPICBaseLow14: - sprintf(temp, "offset 0x%04X, low 14 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kPICBaseHigh16: - sprintf(temp, "offset 0x%04X, high 16 fixup from pic-base of %s plus 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.atom->getDisplayName(), fFromTarget.offset); - break; - case ppc::kAbsLow16: - sprintf(temp, "offset 0x%04X, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsLow14: - sprintf(temp, "offset 0x%04X, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsHigh16: - sprintf(temp, "offset 0x%04X, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kAbsHigh16AddLow: - sprintf(temp, "offset 0x%04X, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case ppc::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case ppc::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case ppc::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - -template <> -const char* Reference<ppc64>::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case ppc64::kNoFixUp: - sprintf(temp, "reference to "); - break; - case ppc64::kFollowOn: - sprintf(temp, "followed by "); - break; - case ppc64::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case ppc64::kPointerWeakImport: - sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); - break; - case ppc64::kPointer: - sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); - break; - case ppc64::kPointerDiff64: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 64-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kPointerDiff32: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 32-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kPointerDiff16: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04llX, 16-bit pointer difference: (&%s%s%s + %u) - (&%s%s%s + %u)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case ppc64::kBranch24WeakImport: - sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case ppc64::kBranch24: - case ppc64::kBranch14: - sprintf(temp, "offset 0x%04llX, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case ppc64::kPICBaseLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kPICBaseLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kPICBaseHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup from pic-base offset 0x%04X to ", fFixUpOffsetInSrc, fFromTarget.offset); - break; - case ppc64::kAbsLow16: - sprintf(temp, "offset 0x%04llX, low 16 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsLow14: - sprintf(temp, "offset 0x%04llX, low 14 fixup to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsHigh16: - sprintf(temp, "offset 0x%04llX, high 16 fixup or to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kAbsHigh16AddLow: - sprintf(temp, "offset 0x%04llX, high 16 fixup add to absolute address of ", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceProbe: - sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceProbeSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case ppc64::kDtraceTypeReference: - sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); - - return temp; -} - - -template <> -const char* Reference<x86_64>::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case x86_64::kNoFixUp: - sprintf(temp, "reference to "); - break; - case x86_64::kFollowOn: - sprintf(temp, "followed by "); - break; - case x86_64::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case x86_64::kPointerWeakImport: - sprintf(temp, "offset 0x%04llX, weak import pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointer: - sprintf(temp, "offset 0x%04llX, pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointer32: - sprintf(temp, "offset 0x%04llX, 32-bit pointer to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointerDiff32: - case x86_64::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - const char* size = (fKind == x86_64::kPointerDiff32) ? "32-bit" : "64-bit"; - sprintf(temp, "offset 0x%04llX, %s pointer difference: (&%s%s%s + 0x%08X) - (&%s%s%s + 0x%08X)", - fFixUpOffsetInSrc, size, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - break; - case x86_64::kPCRel32: - sprintf(temp, "offset 0x%04llX, rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_1: - sprintf(temp, "offset 0x%04llX, rel32-1 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_2: - sprintf(temp, "offset 0x%04llX, rel32-2 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32_4: - sprintf(temp, "offset 0x%04llX, rel32-4 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kBranchPCRel32: - sprintf(temp, "offset 0x%04llX, branch rel32 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kBranchPCRel32WeakImport: - sprintf(temp, "offset 0x%04llX, branch rel32 reference to weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOT: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTWeakImport: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTLoad: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for ", fFixUpOffsetInSrc); - break; - case x86_64::kPCRel32GOTLoadWeakImport: - sprintf(temp, "offset 0x%04llX, rel32 reference to GOT entry for weak imported ", fFixUpOffsetInSrc); - break; - case x86_64::kGOTNoFixUp: - sprintf(temp, "reference to GOT entry for "); - break; - case x86_64::kBranchPCRel8: - sprintf(temp, "offset 0x%04llX, branch rel8 reference to ", fFixUpOffsetInSrc); - break; - case x86_64::kPointerDiff24: - sprintf(temp, "offset 0x%04llX, 24-bit pointer difference: (&%s + 0x%08X) - (&%s + 0x%08X)", - fFixUpOffsetInSrc, this->getTargetDisplayName(), fToTarget.offset, - this->getFromTargetDisplayName(), fFromTarget.offset ); - return temp; - case x86_64::kImageOffset32: - sprintf(temp, "offset 0x%04llX, 32bit offset of ", fFixUpOffsetInSrc); - break; - case x86_64::kSectionOffset24: - sprintf(temp, "offset 0x%04llX, 24-bit section offset of ", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceProbe: - sprintf(temp, "offset 0x%04llX, dtrace static probe ", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceProbeSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe site", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04llX, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case x86_64::kDtraceTypeReference: - sprintf(temp, "offset 0x%04llX, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%llX", this->getTargetOffset()); - - return temp; -} - - -template <> -const char* Reference<arm>::getDescription() const -{ - static char temp[2048]; - switch( fKind ) { - case arm::kNoFixUp: - sprintf(temp, "reference to "); - break; - case arm::kFollowOn: - sprintf(temp, "followed by "); - break; - case arm::kGroupSubordinate: - sprintf(temp, "group subordinate "); - break; - case arm::kPointer: - sprintf(temp, "offset 0x%04X, pointer to ", fFixUpOffsetInSrc); - break; - case arm::kPointerWeakImport: - sprintf(temp, "offset 0x%04X, weak import pointer to ", fFixUpOffsetInSrc); - break; - case arm::kPointerDiff: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 32-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case arm::kPointerDiff12: - { - // by-name references have quoted names - const char* targetQuotes = (&(this->getTarget()) == NULL) ? "\"" : ""; - const char* fromQuotes = (&(this->getFromTarget()) == NULL) ? "\"" : ""; - sprintf(temp, "offset 0x%04X, 12-bit pointer difference: (&%s%s%s + %d) - (&%s%s%s + %d)", - fFixUpOffsetInSrc, targetQuotes, this->getTargetDisplayName(), targetQuotes, fToTarget.offset, - fromQuotes, this->getFromTargetDisplayName(), fromQuotes, fFromTarget.offset ); - return temp; - } - case arm::kReadOnlyPointer: - sprintf(temp, "offset 0x%04X, read-only pointer to ", fFixUpOffsetInSrc); - break; - case arm::kBranch24: - case arm::kThumbBranch22: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to ", fFixUpOffsetInSrc); - break; - case arm::kBranch24WeakImport: - case arm::kThumbBranch22WeakImport: - sprintf(temp, "offset 0x%04X, pc-rel branch fixup to weak imported ", fFixUpOffsetInSrc); - break; - case arm::kDtraceProbe: - sprintf(temp, "offset 0x%04X, dtrace static probe ", fFixUpOffsetInSrc); - break; - case arm::kDtraceProbeSite: - sprintf(temp, "offset 0x%04X, dtrace static probe site", fFixUpOffsetInSrc); - break; - case arm::kDtraceIsEnabledSite: - sprintf(temp, "offset 0x%04X, dtrace static probe is-enabled site", fFixUpOffsetInSrc); - break; - case arm::kDtraceTypeReference: - sprintf(temp, "offset 0x%04X, dtrace type/stability reference", fFixUpOffsetInSrc); - break; - } - // always quote by-name references - if ( fToTargetName != NULL ) { - strcat(temp, "\""); - strcat(temp, fToTargetName); - strcat(temp, "\""); - } - else if ( fToTarget.atom != NULL ) { - strcat(temp, fToTarget.atom->getDisplayName()); - } - else { - strcat(temp, "NULL target"); - } - if ( fToTarget.offset != 0 ) - sprintf(&temp[strlen(temp)], " plus 0x%08X", fToTarget.offset); - - return temp; -} - - -template <> -bool Reference<x86>::isBranch() const -{ - switch ( fKind ) { - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference<x86_64>::isBranch() const -{ - switch ( fKind ) { - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference<ppc>::isBranch() const -{ - switch ( fKind ) { - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference<ppc64>::isBranch() const -{ - switch ( fKind ) { - case ppc64::kBranch24: - case ppc64::kBranch24WeakImport: - return true; - default: - return false; - } -} - -template <> -bool Reference<arm>::isBranch() const -{ - switch ( fKind ) { - case arm::kBranch24: - case arm::kBranch24WeakImport: - case arm::kThumbBranch22: - case arm::kThumbBranch22WeakImport: - return true; - default: - return false; - } -} - - - -}; // namespace relocatable -}; // namespace mach_o - -#endif // __OBJECT_FILE_MACH_O__ diff --git a/src/ld/MachOWriterExecutable.hpp b/src/ld/MachOWriterExecutable.hpp deleted file mode 100644 index 5313669..0000000 --- a/src/ld/MachOWriterExecutable.hpp +++ /dev/null @@ -1,12114 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __EXECUTABLE_MACH_O__ -#define __EXECUTABLE_MACH_O__ - -#include <stdint.h> -#include <stddef.h> -#include <fcntl.h> -#include <sys/time.h> -#include <uuid/uuid.h> -#include <mach/i386/thread_status.h> -#include <mach/ppc/thread_status.h> -#include <CommonCrypto/CommonDigest.h> - -#include <vector> -#include <algorithm> -#include <map> -#include <set> -#include <ext/hash_map> - -#include "ObjectFile.h" -#include "ExecutableFile.h" -#include "Options.h" - -#include "MachOFileAbstraction.hpp" -#include "MachOTrie.hpp" - - -// -// -// To implement architecture xxx, you must write template specializations for the following methods: -// MachHeaderAtom<xxx>::setHeaderInfo() -// ThreadsLoadCommandsAtom<xxx>::getSize() -// ThreadsLoadCommandsAtom<xxx>::copyRawContent() -// Writer<xxx>::addObjectRelocs() -// Writer<xxx>::fixUpReferenceRelocatable() -// Writer<xxx>::fixUpReferenceFinal() -// Writer<xxx>::stubableReference() -// Writer<xxx>::weakImportReferenceKind() -// Writer<xxx>::GOTReferenceKind() -// - - -namespace mach_o { -namespace executable { - -// forward references -template <typename A> class WriterAtom; -template <typename A> class PageZeroAtom; -template <typename A> class CustomStackAtom; -template <typename A> class MachHeaderAtom; -template <typename A> class SegmentLoadCommandsAtom; -template <typename A> class EncryptionLoadCommandsAtom; -template <typename A> class SymbolTableLoadCommandsAtom; -template <typename A> class DyldInfoLoadCommandsAtom; -template <typename A> class ThreadsLoadCommandsAtom; -template <typename A> class DylibIDLoadCommandsAtom; -template <typename A> class RoutinesLoadCommandsAtom; -template <typename A> class DyldLoadCommandsAtom; -template <typename A> class UUIDLoadCommandAtom; -template <typename A> class LinkEditAtom; -template <typename A> class SectionRelocationsLinkEditAtom; -template <typename A> class CompressedRebaseInfoLinkEditAtom; -template <typename A> class CompressedBindingInfoLinkEditAtom; -template <typename A> class CompressedWeakBindingInfoLinkEditAtom; -template <typename A> class CompressedLazyBindingInfoLinkEditAtom; -template <typename A> class CompressedExportInfoLinkEditAtom; -template <typename A> class LocalRelocationsLinkEditAtom; -template <typename A> class ExternalRelocationsLinkEditAtom; -template <typename A> class SymbolTableLinkEditAtom; -template <typename A> class SegmentSplitInfoLoadCommandsAtom; -template <typename A> class SegmentSplitInfoContentAtom; -template <typename A> class IndirectTableLinkEditAtom; -template <typename A> class ModuleInfoLinkEditAtom; -template <typename A> class StringsLinkEditAtom; -template <typename A> class LoadCommandsPaddingAtom; -template <typename A> class UnwindInfoAtom; -template <typename A> class StubAtom; -template <typename A> class StubHelperAtom; -template <typename A> class ClassicStubHelperAtom; -template <typename A> class HybridStubHelperAtom; -template <typename A> class HybridStubHelperHelperAtom; -template <typename A> class FastStubHelperAtom; -template <typename A> class FastStubHelperHelperAtom; -template <typename A> class LazyPointerAtom; -template <typename A> class NonLazyPointerAtom; -template <typename A> class DylibLoadCommandsAtom; -template <typename A> class BranchIslandAtom; - - -// SectionInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes -class SectionInfo : public ObjectFile::Section { -public: - SectionInfo() : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), - fIndirectSymbolOffset(0), fAlignment(0), fAllLazyPointers(false), - fAllLazyDylibPointers(false),fAllNonLazyPointers(false), fAllStubs(false), - fAllSelfModifyingStubs(false), fAllStubHelpers(false), - fAllZeroFill(false), fVirtualSection(false), - fHasTextLocalRelocs(false), fHasTextExternalRelocs(false) - { fSegmentName[0] = '\0'; fSectionName[0] = '\0'; } - void setIndex(unsigned int index) { fIndex=index; } - std::vector<ObjectFile::Atom*> fAtoms; - char fSegmentName[20]; - char fSectionName[20]; - uint64_t fFileOffset; - uint64_t fSize; - uint32_t fRelocCount; - uint32_t fRelocOffset; - uint32_t fIndirectSymbolOffset; - uint8_t fAlignment; - bool fAllLazyPointers; - bool fAllLazyDylibPointers; - bool fAllNonLazyPointers; - bool fAllStubs; - bool fAllSelfModifyingStubs; - bool fAllStubHelpers; - bool fAllZeroFill; - bool fVirtualSection; - bool fHasTextLocalRelocs; - bool fHasTextExternalRelocs; -}; - -// SegmentInfo should be nested inside Writer, but I can't figure out how to make the type accessible to the Atom classes -class SegmentInfo -{ -public: - SegmentInfo(uint64_t pageSize) : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), - fBaseAddress(0), fSize(0), fPageSize(pageSize), fFixedAddress(false), - fIndependentAddress(false), fHasLoadCommand(true) { fName[0] = '\0'; } - std::vector<class SectionInfo*> fSections; - char fName[20]; - uint32_t fInitProtection; - uint32_t fMaxProtection; - uint64_t fFileOffset; - uint64_t fFileSize; - uint64_t fBaseAddress; - uint64_t fSize; - uint64_t fPageSize; - bool fFixedAddress; - bool fIndependentAddress; - bool fHasLoadCommand; -}; - - -struct RebaseInfo { - RebaseInfo(uint8_t t, uint64_t addr) : fType(t), fAddress(addr) {} - uint8_t fType; - uint64_t fAddress; - // for sorting - int operator<(const RebaseInfo& rhs) const { - // sort by type, then address - if ( this->fType != rhs.fType ) - return (this->fType < rhs.fType ); - return (this->fAddress < rhs.fAddress ); - } -}; - -struct BindingInfo { - BindingInfo(uint8_t t, int ord, const char* sym, bool weak_import, uint64_t addr, int64_t addend) - : fType(t), fFlags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ), fLibraryOrdinal(ord), - fSymbolName(sym), fAddress(addr), fAddend(addend) {} - BindingInfo(uint8_t t, const char* sym, bool non_weak_definition, uint64_t addr, int64_t addend) - : fType(t), fFlags(non_weak_definition ? BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : 0 ), fLibraryOrdinal(0), - fSymbolName(sym), fAddress(addr), fAddend(addend) {} - uint8_t fType; - uint8_t fFlags; - int fLibraryOrdinal; - const char* fSymbolName; - uint64_t fAddress; - int64_t fAddend; - - // for sorting - int operator<(const BindingInfo& rhs) const { - // sort by library, symbol, type, then address - if ( this->fLibraryOrdinal != rhs.fLibraryOrdinal ) - return (this->fLibraryOrdinal < rhs.fLibraryOrdinal ); - if ( this->fSymbolName != rhs.fSymbolName ) - return ( strcmp(this->fSymbolName, rhs.fSymbolName) < 0 ); - if ( this->fType != rhs.fType ) - return (this->fType < rhs.fType ); - return (this->fAddress < rhs.fAddress ); - } -}; - - -class ByteStream { -private: - std::vector<uint8_t> fData; -public: - std::vector<uint8_t>& bytes() { return fData; } - unsigned long size() const { return fData.size(); } - void reserve(unsigned long l) { fData.reserve(l); } - const uint8_t* start() const { return &fData[0]; } - - void append_uleb128(uint64_t value) { - uint8_t byte; - do { - byte = value & 0x7F; - value &= ~0x7F; - if ( value != 0 ) - byte |= 0x80; - fData.push_back(byte); - value = value >> 7; - } while( byte >= 0x80 ); - } - - void append_sleb128(int64_t value) { - bool isNeg = ( value < 0 ); - uint8_t byte; - bool more; - do { - byte = value & 0x7F; - value = value >> 7; - if ( isNeg ) - more = ( (value != -1) || ((byte & 0x40) == 0) ); - else - more = ( (value != 0) || ((byte & 0x40) != 0) ); - if ( more ) - byte |= 0x80; - fData.push_back(byte); - } - while( more ); - } - - void append_string(const char* str) { - for (const char* s = str; *s != '\0'; ++s) - fData.push_back(*s); - fData.push_back('\0'); - } - - void append_byte(uint8_t byte) { - fData.push_back(byte); - } - - static unsigned int uleb128_size(uint64_t value) { - uint32_t result = 0; - do { - value = value >> 7; - ++result; - } while ( value != 0 ); - return result; - } - - void pad_to_size(unsigned int alignment) { - while ( (fData.size() % alignment) != 0 ) - fData.push_back(0); - } -}; - - -template <typename A> -class Writer : public ExecutableFile::Writer -{ -public: - Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries); - virtual ~Writer(); - - virtual const char* getPath() { return fFilePath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return fWriterSynthesizedAtoms; } - virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector<Stab>* getStabs() { return NULL; } - - virtual ObjectFile::Atom& makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses); - virtual class ObjectFile::Atom* getUndefinedProxyAtom(const char* name); - virtual void addSynthesizedAtoms(const std::vector<class ObjectFile::Atom*>& existingAtoms, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool biggerThanTwoGigs, - uint32_t dylibSymbolCount, - std::vector<class ObjectFile::Atom*>& newAtoms); - virtual uint64_t write(std::vector<class ObjectFile::Atom*>& atoms, - std::vector<class ObjectFile::Reader::Stab>& stabs, - class ObjectFile::Atom* entryPointAtom, - bool createUUID, bool canScatter, - ObjectFile::Reader::CpuConstraint cpuConstraint, - std::set<const class ObjectFile::Atom*>& atomsThatOverrideWeak, - bool hasExternalWeakDefinitions); - -private: - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - - enum RelocKind { kRelocNone, kRelocInternal, kRelocExternal }; - - void assignFileOffsets(); - void synthesizeStubs(const std::vector<class ObjectFile::Atom*>& existingAtoms, - std::vector<class ObjectFile::Atom*>& newAtoms); - void synthesizeKextGOT(const std::vector<class ObjectFile::Atom*>& existingAtoms, - std::vector<class ObjectFile::Atom*>& newAtoms); - void createSplitSegContent(); - void synthesizeUnwindInfoTable(); - void insertDummyStubs(); - void partitionIntoSections(); - bool addBranchIslands(); - bool createBranchIslands(); - bool isBranchThatMightNeedIsland(uint8_t kind); - uint32_t textSizeWhenMightNeedBranchIslands(); - uint32_t maxDistanceBetweenIslands(); - void adjustLoadCommandsAndPadding(); - void createDynamicLinkerCommand(); - void createDylibCommands(); - void buildLinkEdit(); - const char* getArchString(); - void writeMap(); - uint64_t writeAtoms(); - void writeNoOps(int fd, uint32_t from, uint32_t to); - void copyNoOps(uint8_t* from, uint8_t* to); - bool segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to); - void addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref); - void collectExportedAndImportedAndLocalAtoms(); - void setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count); - void addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); - void addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name); - void buildSymbolTable(); - bool stringsNeedLabelsInObjects(); - const char* symbolTableName(const ObjectFile::Atom* atom); - void setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry); - void setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry); - void setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry); - void copyNlistRange(const std::vector<macho_nlist<P> >& entries, uint32_t startIndex); - uint64_t getAtomLoadAddress(const ObjectFile::Atom* atom); - uint8_t ordinalForLibrary(ObjectFile::Reader* file); - bool targetRequiresWeakBinding(const ObjectFile::Atom& target); - int compressedOrdinalForImortedAtom(ObjectFile::Atom* target); - bool shouldExport(const ObjectFile::Atom& atom) const; - void buildFixups(); - void adjustLinkEditSections(); - void buildObjectFileFixups(); - void buildExecutableFixups(); - bool preboundLazyPointerType(uint8_t* type); - uint64_t relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const; - void fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; - void fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const; - void fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, - uint8_t buffer[], bool finalLinkedImage) const; - uint32_t symbolIndex(ObjectFile::Atom& atom); - bool makesExternalRelocatableReference(ObjectFile::Atom& target) const; - uint32_t addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref); - uint32_t addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref); - uint8_t getRelocPointerSize(); - uint64_t maxAddress(); - bool stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref); - bool GOTReferenceKind(uint8_t kind); - bool optimizableGOTReferenceKind(uint8_t kind); - bool weakImportReferenceKind(uint8_t kind); - unsigned int collectStabs(); - uint64_t valueForStab(const ObjectFile::Reader::Stab& stab); - uint32_t stringOffsetForStab(const ObjectFile::Reader::Stab& stab); - uint8_t sectionIndexForStab(const ObjectFile::Reader::Stab& stab); - void addStabs(uint32_t startIndex); - RelocKind relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const; - bool illegalRelocInFinalLinkedImage(const ObjectFile::Reference&); - bool generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); - bool generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection); - bool mightNeedPadSegment(); - void scanForAbsoluteReferences(); - bool needsModuleTable(); - void optimizeDylibReferences(); - bool indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const; - - struct DirectLibrary { - class ObjectFile::Reader* fLibrary; - bool fWeak; - bool fReExport; - }; - - friend class WriterAtom<A>; - friend class PageZeroAtom<A>; - friend class CustomStackAtom<A>; - friend class MachHeaderAtom<A>; - friend class SegmentLoadCommandsAtom<A>; - friend class EncryptionLoadCommandsAtom<A>; - friend class SymbolTableLoadCommandsAtom<A>; - friend class DyldInfoLoadCommandsAtom<A>; - friend class ThreadsLoadCommandsAtom<A>; - friend class DylibIDLoadCommandsAtom<A>; - friend class RoutinesLoadCommandsAtom<A>; - friend class DyldLoadCommandsAtom<A>; - friend class UUIDLoadCommandAtom<A>; - friend class LinkEditAtom<A>; - friend class SectionRelocationsLinkEditAtom<A>; - friend class CompressedRebaseInfoLinkEditAtom<A>; - friend class CompressedBindingInfoLinkEditAtom<A>; - friend class CompressedWeakBindingInfoLinkEditAtom<A>; - friend class CompressedLazyBindingInfoLinkEditAtom<A>; - friend class CompressedExportInfoLinkEditAtom<A>; - friend class LocalRelocationsLinkEditAtom<A>; - friend class ExternalRelocationsLinkEditAtom<A>; - friend class SymbolTableLinkEditAtom<A>; - friend class SegmentSplitInfoLoadCommandsAtom<A>; - friend class SegmentSplitInfoContentAtom<A>; - friend class IndirectTableLinkEditAtom<A>; - friend class ModuleInfoLinkEditAtom<A>; - friend class StringsLinkEditAtom<A>; - friend class LoadCommandsPaddingAtom<A>; - friend class UnwindInfoAtom<A>; - friend class StubAtom<A>; - friend class StubHelperAtom<A>; - friend class ClassicStubHelperAtom<A>; - friend class HybridStubHelperAtom<A>; - friend class FastStubHelperAtom<A>; - friend class FastStubHelperHelperAtom<A>; - friend class HybridStubHelperHelperAtom<A>; - friend class LazyPointerAtom<A>; - friend class NonLazyPointerAtom<A>; - friend class DylibLoadCommandsAtom<A>; - friend class BranchIslandAtom<A>; - - const char* fFilePath; - Options& fOptions; - std::vector<class ObjectFile::Atom*>* fAllAtoms; - std::vector<class ObjectFile::Reader::Stab>* fStabs; - std::set<const class ObjectFile::Atom*>* fRegularDefAtomsThatOverrideADylibsWeakDef; - class SectionInfo* fLoadCommandsSection; - class SegmentInfo* fLoadCommandsSegment; - class MachHeaderAtom<A>* fMachHeaderAtom; - class EncryptionLoadCommandsAtom<A>* fEncryptionLoadCommand; - class SegmentLoadCommandsAtom<A>* fSegmentCommands; - class SymbolTableLoadCommandsAtom<A>* fSymbolTableCommands; - class LoadCommandsPaddingAtom<A>* fHeaderPadding; - class UnwindInfoAtom<A>* fUnwindInfoAtom; - class UUIDLoadCommandAtom<A>* fUUIDAtom; - std::vector<class ObjectFile::Atom*> fWriterSynthesizedAtoms; - std::vector<SegmentInfo*> fSegmentInfos; - class SegmentInfo* fPadSegmentInfo; - class ObjectFile::Atom* fEntryPoint; - class ObjectFile::Atom* fDyldClassicHelperAtom; - class ObjectFile::Atom* fDyldCompressedHelperAtom; - class ObjectFile::Atom* fDyldLazyDylibHelper; - std::map<class ObjectFile::Reader*, DylibLoadCommandsAtom<A>*> fLibraryToLoadCommand; - std::map<class ObjectFile::Reader*, uint32_t> fLibraryToOrdinal; - std::map<class ObjectFile::Reader*, class ObjectFile::Reader*> fLibraryAliases; - std::set<class ObjectFile::Reader*> fForcedWeakImportReaders; - std::vector<class ObjectFile::Atom*> fExportedAtoms; - std::vector<class ObjectFile::Atom*> fImportedAtoms; - std::vector<class ObjectFile::Atom*> fLocalSymbolAtoms; - std::vector<macho_nlist<P> > fLocalExtraLabels; - std::vector<macho_nlist<P> > fGlobalExtraLabels; - std::map<ObjectFile::Atom*, uint32_t> fAtomToSymbolIndex; - class SectionRelocationsLinkEditAtom<A>* fSectionRelocationsAtom; - class CompressedRebaseInfoLinkEditAtom<A>* fCompressedRebaseInfoAtom; - class CompressedBindingInfoLinkEditAtom<A>* fCompressedBindingInfoAtom; - class CompressedWeakBindingInfoLinkEditAtom<A>* fCompressedWeakBindingInfoAtom; - class CompressedLazyBindingInfoLinkEditAtom<A>* fCompressedLazyBindingInfoAtom; - class CompressedExportInfoLinkEditAtom<A>* fCompressedExportInfoAtom; - class LocalRelocationsLinkEditAtom<A>* fLocalRelocationsAtom; - class ExternalRelocationsLinkEditAtom<A>* fExternalRelocationsAtom; - class SymbolTableLinkEditAtom<A>* fSymbolTableAtom; - class SegmentSplitInfoContentAtom<A>* fSplitCodeToDataContentAtom; - class IndirectTableLinkEditAtom<A>* fIndirectTableAtom; - class ModuleInfoLinkEditAtom<A>* fModuleInfoAtom; - class StringsLinkEditAtom<A>* fStringsAtom; - class PageZeroAtom<A>* fPageZeroAtom; - class NonLazyPointerAtom<A>* fFastStubGOTAtom; - macho_nlist<P>* fSymbolTable; - std::vector<macho_relocation_info<P> > fSectionRelocs; - std::vector<macho_relocation_info<P> > fInternalRelocs; - std::vector<macho_relocation_info<P> > fExternalRelocs; - std::vector<RebaseInfo> fRebaseInfo; - std::vector<BindingInfo> fBindingInfo; - std::vector<BindingInfo> fWeakBindingInfo; - std::map<const ObjectFile::Atom*,ObjectFile::Atom*> fStubsMap; - std::map<ObjectFile::Atom*,ObjectFile::Atom*> fGOTMap; - std::vector<class StubAtom<A>*> fAllSynthesizedStubs; - std::vector<ObjectFile::Atom*> fAllSynthesizedStubHelpers; - std::vector<class LazyPointerAtom<A>*> fAllSynthesizedLazyPointers; - std::vector<class LazyPointerAtom<A>*> fAllSynthesizedLazyDylibPointers; - std::vector<class NonLazyPointerAtom<A>*> fAllSynthesizedNonLazyPointers; - uint32_t fSymbolTableCount; - uint32_t fSymbolTableStabsCount; - uint32_t fSymbolTableStabsStartIndex; - uint32_t fSymbolTableLocalCount; - uint32_t fSymbolTableLocalStartIndex; - uint32_t fSymbolTableExportCount; - uint32_t fSymbolTableExportStartIndex; - uint32_t fSymbolTableImportCount; - uint32_t fSymbolTableImportStartIndex; - uint32_t fLargestAtomSize; - uint32_t fDylibSymbolCountUpperBound; - bool fEmitVirtualSections; - bool fHasWeakExports; - bool fReferencesWeakImports; - bool fCanScatter; - bool fWritableSegmentPastFirst4GB; - bool fNoReExportedDylibs; - bool fBiggerThanTwoGigs; - bool fSlideable; - bool fHasThumbBranches; - std::map<const ObjectFile::Atom*,bool> fWeakImportMap; - std::set<const ObjectFile::Reader*> fDylibReadersWithNonWeakImports; - std::set<const ObjectFile::Reader*> fDylibReadersWithWeakImports; - SegmentInfo* fFirstWritableSegment; - ObjectFile::Reader::CpuConstraint fCpuConstraint; - uint32_t fAnonNameIndex; -}; - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress) - : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {} - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return fReadable; } - virtual bool isContentWritable() const { return fWritable; } - virtual bool isContentExecutable() const { return fExecutable; } - virtual bool hasFixedAddress() const { return fFixedAddress; } - - static Segment fgTextSegment; - static Segment fgPageZeroSegment; - static Segment fgLinkEditSegment; - static Segment fgStackSegment; - static Segment fgImportSegment; - static Segment fgROImportSegment; - static Segment fgDataSegment; - static Segment fgObjCSegment; - static Segment fgHeaderSegment; - - -private: - const char* fName; - const bool fReadable; - const bool fWritable; - const bool fExecutable; - const bool fFixedAddress; -}; - -Segment Segment::fgPageZeroSegment("__PAGEZERO", false, false, false, true); -Segment Segment::fgTextSegment("__TEXT", true, false, true, false); -Segment Segment::fgLinkEditSegment("__LINKEDIT", true, false, false, false); -Segment Segment::fgStackSegment("__UNIXSTACK", true, true, false, true); -Segment Segment::fgImportSegment("__IMPORT", true, true, true, false); -Segment Segment::fgROImportSegment("__IMPORT", true, false, true, false); -Segment Segment::fgDataSegment("__DATA", true, true, false, false); -Segment Segment::fgObjCSegment("__OBJC", true, true, false, false); -Segment Segment::fgHeaderSegment("__HEADER", true, false, true, false); - - -template <typename A> -class WriterAtom : public ObjectFile::Atom -{ -public: - enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy }; - WriterAtom(Writer<A>& writer, Segment& segment) : fWriter(writer), fSegment(segment) { } - - virtual ObjectFile::Reader* getFile() const { return &fWriter; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return NULL; } - virtual const char* getDisplayName() const { return this->getName(); } - virtual Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return true; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return fgEmptyReferenceList; } - virtual bool mustRemainInSection() const { return true; } - virtual ObjectFile::Segment& getSegment() const { return fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return 0; } - virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(2); } - virtual void copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; } - virtual void setScope(Scope) { } - - -protected: - virtual ~WriterAtom() {} - typedef typename A::P P; - typedef typename A::P::E E; - - static Segment& headerSegment(Writer<A>& writer) { return (writer.fOptions.outputKind()==Options::kPreload) - ? Segment::fgHeaderSegment : Segment::fgTextSegment; } - - static std::vector<ObjectFile::Reference*> fgEmptyReferenceList; - - Writer<A>& fWriter; - Segment& fSegment; -}; - -template <typename A> std::vector<ObjectFile::Reference*> WriterAtom<A>::fgEmptyReferenceList; - - -template <typename A> -class PageZeroAtom : public WriterAtom<A> -{ -public: - PageZeroAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgPageZeroSegment), - fSize(fWriter.fOptions.zeroPageSize()) {} - virtual const char* getDisplayName() const { return "page zero content"; } - virtual bool isZeroFill() const { return true; } - virtual uint64_t getSize() const { return fSize; } - virtual const char* getSectionName() const { return "._zeropage"; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - void setSize(uint64_t size) { fSize = size; } -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - uint64_t fSize; -}; - - -template <typename A> -class DsoHandleAtom : public WriterAtom<A> -{ -public: - DsoHandleAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment) {} - virtual const char* getName() const { return "___dso_handle"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual uint64_t getSize() const { return 0; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } - virtual const char* getSectionName() const { return "._mach_header"; } - virtual void copyRawContent(uint8_t buffer[]) const {} -}; - - -template <typename A> -class MachHeaderAtom : public WriterAtom<A> -{ -public: - MachHeaderAtom(Writer<A>& writer) : WriterAtom<A>(writer, headerSegment(writer)) {} - virtual const char* getName() const; - virtual const char* getDisplayName() const; - virtual ObjectFile::Atom::Scope getScope() const; - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const; - virtual uint64_t getSize() const { return sizeof(macho_header<typename A::P>); } - 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<A>::fWriter; - typedef typename A::P P; - void setHeaderInfo(macho_header<typename A::P>& header) const; -}; - -template <typename A> -class CustomStackAtom : public WriterAtom<A> -{ -public: - CustomStackAtom(Writer<A>& writer); - virtual const char* getDisplayName() const { return "custom stack content"; } - virtual bool isZeroFill() const { return true; } - virtual uint64_t getSize() const { return fWriter.fOptions.customStackSize(); } - virtual const char* getSectionName() const { return "._stack"; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(12); } -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - static bool stackGrowsDown(); -}; - -template <typename A> -class LoadCommandAtom : public WriterAtom<A> -{ -protected: - LoadCommandAtom(Writer<A>& writer) : WriterAtom<A>(writer, headerSegment(writer)), fOrdinal(fgCurrentOrdinal++) {} - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(log2(sizeof(typename A::P::uint_t))); } - virtual const char* getSectionName() const { return "._load_commands"; } - virtual uint32_t getOrdinal() const { return fOrdinal; } - static uint64_t alignedSize(uint64_t size); -protected: - uint32_t fOrdinal; - static uint32_t fgCurrentOrdinal; -}; - -template <typename A> uint32_t LoadCommandAtom<A>::fgCurrentOrdinal = 0; - -template <typename A> -class SegmentLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - SegmentLoadCommandsAtom(Writer<A>& writer) - : LoadCommandAtom<A>(writer), fCommandCount(0), fSize(0) - { writer.fSegmentCommands = this; } - virtual const char* getDisplayName() const { return "segment load commands"; } - virtual uint64_t getSize() const { return fSize; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void computeSize(); - void setup(); - unsigned int commandCount() { return fCommandCount; } -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - unsigned int fCommandCount; - uint32_t fSize; -}; - - -template <typename A> -class SymbolTableLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - SymbolTableLoadCommandsAtom(Writer<A>&); - virtual const char* getDisplayName() const { return "symbol table load commands"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - unsigned int commandCount(); - void needDynamicTable(); -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - bool fNeedsDynamicSymbolTable; - macho_symtab_command<typename A::P> fSymbolTable; - macho_dysymtab_command<typename A::P> fDynamicSymbolTable; -}; - -template <typename A> -class ThreadsLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - ThreadsLoadCommandsAtom(Writer<A>& writer) - : LoadCommandAtom<A>(writer) {} - virtual const char* getDisplayName() const { return "thread load commands"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - uint8_t* fBuffer; - uint32_t fBufferSize; -}; - -template <typename A> -class DyldLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - DyldLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {} - virtual const char* getDisplayName() const { return "dyld load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class SegmentSplitInfoLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - SegmentSplitInfoLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {} - virtual const char* getDisplayName() const { return "segment split info load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class AllowableClientLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - AllowableClientLoadCommandsAtom(Writer<A>& writer, const char* client) : - LoadCommandAtom<A>(writer), clientString(client) {} - virtual const char* getDisplayName() const { return "allowable_client load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - const char* clientString; -}; - -template <typename A> -class DylibLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - DylibLoadCommandsAtom(Writer<A>& writer, ExecutableFile::DyLibUsed& info) - : LoadCommandAtom<A>(writer), fInfo(info), - fOptimizedAway(false) { if (fInfo.options.fLazyLoad) this->fOrdinal += 256; } - virtual const char* getDisplayName() const { return "dylib load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void optimizeAway() { fOptimizedAway = true; } - bool linkedWeak() { return fInfo.options.fWeakImport; } -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - ExecutableFile::DyLibUsed fInfo; - bool fOptimizedAway; -}; - -template <typename A> -class DylibIDLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - DylibIDLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {} - virtual const char* getDisplayName() const { return "dylib ID load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class RoutinesLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - RoutinesLoadCommandsAtom(Writer<A>& writer) : LoadCommandAtom<A>(writer) {} - virtual const char* getDisplayName() const { return "routines load command"; } - virtual uint64_t getSize() const { return sizeof(macho_routines_command<typename A::P>); } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class SubUmbrellaLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - SubUmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name) - : LoadCommandAtom<A>(writer), fName(name) {} - virtual const char* getDisplayName() const { return "sub-umbrella load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - typedef typename A::P P; - const char* fName; -}; - -template <typename A> -class SubLibraryLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - SubLibraryLoadCommandsAtom(Writer<A>& writer, const char* nameStart, int nameLen) - : LoadCommandAtom<A>(writer), fNameStart(nameStart), fNameLength(nameLen) {} - virtual const char* getDisplayName() const { return "sub-library load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - const char* fNameStart; - int fNameLength; -}; - -template <typename A> -class UmbrellaLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - UmbrellaLoadCommandsAtom(Writer<A>& writer, const char* name) - : LoadCommandAtom<A>(writer), fName(name) {} - virtual const char* getDisplayName() const { return "umbrella load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - const char* fName; -}; - -template <typename A> -class UUIDLoadCommandAtom : public LoadCommandAtom<A> -{ -public: - UUIDLoadCommandAtom(Writer<A>& writer) - : LoadCommandAtom<A>(writer), fEmit(false) {} - virtual const char* getDisplayName() const { return "uuid load command"; } - virtual uint64_t getSize() const { return fEmit ? sizeof(macho_uuid_command<typename A::P>) : 0; } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual void generate(); - void setContent(const uint8_t uuid[16]); - const uint8_t* getUUID() { return fUUID; } -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - uuid_t fUUID; - bool fEmit; -}; - - -template <typename A> -class RPathLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - RPathLoadCommandsAtom(Writer<A>& writer, const char* path) - : LoadCommandAtom<A>(writer), fPath(path) {} - virtual const char* getDisplayName() const { return "rpath load command"; } - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - const char* fPath; -}; - -template <typename A> -class EncryptionLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - EncryptionLoadCommandsAtom(Writer<A>& writer) - : LoadCommandAtom<A>(writer), fStartOffset(0), - fEndOffset(0) {} - virtual const char* getDisplayName() const { return "encryption info load command"; } - virtual uint64_t getSize() const { return sizeof(macho_encryption_info_command<typename A::P>); } - virtual void copyRawContent(uint8_t buffer[]) const; - void setStartEncryptionOffset(uint32_t off) { fStartOffset = off; } - void setEndEncryptionOffset(uint32_t off) { fEndOffset = off; } -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - uint32_t fStartOffset; - uint32_t fEndOffset; -}; - -template <typename A> -class DyldInfoLoadCommandsAtom : public LoadCommandAtom<A> -{ -public: - DyldInfoLoadCommandsAtom(Writer<A>& writer) - : LoadCommandAtom<A>(writer) {} - virtual const char* getDisplayName() const { return "dyld info load command"; } - virtual uint64_t getSize() const { return sizeof(macho_dyld_info_command<typename A::P>); } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - - -template <typename A> -class LoadCommandsPaddingAtom : public WriterAtom<A> -{ -public: - LoadCommandsPaddingAtom(Writer<A>& writer) - : WriterAtom<A>(writer, headerSegment(writer)), fSize(0) {} - virtual const char* getDisplayName() const { return "header padding"; } - virtual uint64_t getSize() const { return fSize; } - virtual const char* getSectionName() const { return "._load_cmds_pad"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void setSize(uint64_t newSize); -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - uint64_t fSize; -}; - -template <typename A> -class MinimalTextAtom : public WriterAtom<A> -{ -public: - MinimalTextAtom(Writer<A>& writer) - : WriterAtom<A>(writer, headerSegment(writer)) {} - virtual const char* getDisplayName() const { return "minimal text"; } - virtual uint64_t getSize() const { return 0; } - virtual const char* getSectionName() const { return "__text"; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - -private: - using WriterAtom<A>::fWriter; -}; - - -template <typename A> -class UnwindInfoAtom : public WriterAtom<A> -{ -public: - UnwindInfoAtom(Writer<A>& writer) : WriterAtom<A>(writer, Segment::fgTextSegment), - fHeaderSize(0), fPagesSize(0), fAlignment(4) {} - virtual const char* getName() const { return "unwind info"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual uint64_t getSize() const { return fHeaderSize+fPagesSize; } - virtual ObjectFile::Alignment getAlignment() const { return fAlignment; } - virtual const char* getSectionName() const { return "__unwind_info"; } - virtual uint32_t getOrdinal() const { return 1; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)fReferences; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsda, - ObjectFile::Atom* personalityPointer); - void generate(); - -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - struct Info { ObjectFile::Atom* func; ObjectFile::Atom* fde; ObjectFile::Atom* lsda; uint32_t lsdaOffset; ObjectFile::Atom* personalityPointer; uint32_t encoding; }; - struct LSDAEntry { ObjectFile::Atom* func; ObjectFile::Atom* lsda; uint32_t lsdaOffset; }; - struct RegFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fde; }; - struct CompressedFixUp { uint8_t* contentPointer; ObjectFile::Atom* func; ObjectFile::Atom* fromFunc; }; - struct CompressedEncodingFixUp { uint8_t* contentPointer; ObjectFile::Atom* fde; }; - - bool encodingMeansUseDwarf(compact_unwind_encoding_t encoding); - void compressDuplicates(std::vector<Info>& uniqueInfos); - void findCommonEncoding(const std::vector<Info>& uniqueInfos, std::map<uint32_t, unsigned int>& commonEncodings); - void makeLsdaIndex(const std::vector<Info>& uniqueInfos, std::map<ObjectFile::Atom*, uint32_t>& lsdaIndexOffsetMap); - unsigned int makeRegularSecondLevelPage(const std::vector<Info>& uniqueInfos, uint32_t pageSize, unsigned int endIndex, - uint8_t*& pageEnd); - unsigned int makeCompressedSecondLevelPage(const std::vector<Info>& uniqueInfos, - const std::map<uint32_t,unsigned int> commonEncodings, - uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd); - void makePersonalityIndex(std::vector<Info>& uniqueInfos); - - - uint32_t fHeaderSize; - uint32_t fPagesSize; - uint8_t* fHeaderContent; - uint8_t* fPagesContent; - uint8_t* fPagesContentForDelete; - ObjectFile::Alignment fAlignment; - std::vector<Info> fInfos; - std::map<ObjectFile::Atom*, uint32_t> fPersonalityIndexMap; - std::vector<LSDAEntry> fLSDAIndex; - std::vector<RegFixUp> fRegFixUps; - std::vector<CompressedFixUp> fCompressedFixUps; - std::vector<CompressedEncodingFixUp> fCompressedEncodingFixUps; - std::vector<ObjectFile::Reference*> fReferences; -}; - - - -template <typename A> -class LinkEditAtom : public WriterAtom<A> -{ -public: - LinkEditAtom(Writer<A>& writer) : WriterAtom<A>(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 <typename A> uint32_t LinkEditAtom<A>::fgCurrentOrdinal = 0; - -template <typename A> -class SectionRelocationsLinkEditAtom : public LinkEditAtom<A> -{ -public: - SectionRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "section relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._section_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class CompressedInfoLinkEditAtom : public LinkEditAtom<A> -{ -public: - CompressedInfoLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { } - virtual uint64_t getSize() const { return fEncodedData.size(); } - virtual void copyRawContent(uint8_t buffer[]) const { memcpy(buffer, fEncodedData.start(), fEncodedData.size()); } -protected: - typedef typename A::P::uint_t pint_t; - ByteStream fEncodedData; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - - - -template <typename A> -class CompressedRebaseInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A> -{ -public: - CompressedRebaseInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "compressed rebase info"; } - virtual const char* getSectionName() const { return "._rebase info"; } - void encode(); -private: - using CompressedInfoLinkEditAtom<A>::fEncodedData; - using CompressedInfoLinkEditAtom<A>::fWriter; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - -template <typename A> -class CompressedBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A> -{ -public: - CompressedBindingInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "compressed binding info"; } - virtual const char* getSectionName() const { return "._binding info"; } - void encode(); -private: - using CompressedInfoLinkEditAtom<A>::fWriter; - using CompressedInfoLinkEditAtom<A>::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - -template <typename A> -class CompressedWeakBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A> -{ -public: - CompressedWeakBindingInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "compressed weak binding info"; } - virtual const char* getSectionName() const { return "._wkbinding info"; } - void encode(); -private: - using CompressedInfoLinkEditAtom<A>::fWriter; - using CompressedInfoLinkEditAtom<A>::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - -template <typename A> -class CompressedLazyBindingInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A> -{ -public: - CompressedLazyBindingInfoLinkEditAtom(Writer<A>& writer) : CompressedInfoLinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "compressed lazy binding info"; } - virtual const char* getSectionName() const { return "._lzbinding info"; } - void encode(); -private: - std::vector<uint32_t> fStarts; - - using CompressedInfoLinkEditAtom<A>::fWriter; - using CompressedInfoLinkEditAtom<A>::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; -}; - - -template <typename A> -class CompressedExportInfoLinkEditAtom : public CompressedInfoLinkEditAtom<A> -{ -public: - CompressedExportInfoLinkEditAtom(Writer<A>& writer) - : CompressedInfoLinkEditAtom<A>(writer), fStartNode(strdup("")) { } - virtual const char* getDisplayName() const { return "compressed export info"; } - virtual const char* getSectionName() const { return "._export info"; } - void encode(); -private: - using WriterAtom<A>::fWriter; - using CompressedInfoLinkEditAtom<A>::fEncodedData; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - struct node; - - struct edge - { - edge(const char* s, struct node* n) : fSubString(s), fChild(n) { } - ~edge() { } - const char* fSubString; - struct node* fChild; - - }; - - struct node - { - node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), - fHaveExportInfo(false), fTrieOffset(0) {} - ~node() { } - const char* fCummulativeString; - std::vector<edge> fChildren; - uint64_t fAddress; - uint32_t fFlags; - bool fOrdered; - bool fHaveExportInfo; - uint32_t fTrieOffset; - - void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { - const char* partialStr = &fullStr[strlen(fCummulativeString)]; - for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - int subStringLen = strlen(e.fSubString); - if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { - // already have matching edge, go down that path - e.fChild->addSymbol(fullStr, address, flags); - return; - } - else { - for (int i=subStringLen-1; i > 0; --i) { - if ( strncmp(e.fSubString, partialStr, i) == 0 ) { - // found a common substring, splice in new node - // was A -> C, now A -> B -> C - char* bNodeCummStr = strdup(e.fChild->fCummulativeString); - bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; - //node* aNode = this; - node* bNode = new node(bNodeCummStr); - node* cNode = e.fChild; - char* abEdgeStr = strdup(e.fSubString); - abEdgeStr[i] = '\0'; - char* bcEdgeStr = strdup(&e.fSubString[i]); - edge& abEdge = e; - abEdge.fSubString = abEdgeStr; - abEdge.fChild = bNode; - edge bcEdge(bcEdgeStr, cNode); - bNode->fChildren.push_back(bcEdge); - bNode->addSymbol(fullStr, address, flags); - return; - } - } - } - } - // no commonality with any existing child, make a new edge that is this whole string - node* newNode = new node(strdup(fullStr)); - edge newEdge(strdup(partialStr), newNode); - fChildren.push_back(newEdge); - newNode->fAddress = address; - newNode->fFlags = flags; - newNode->fHaveExportInfo = true; - } - - void addOrderedNodes(const char* name, std::vector<node*>& orderedNodes) { - if ( !fOrdered ) { - orderedNodes.push_back(this); - //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); - fOrdered = true; - } - const char* partialStr = &name[strlen(fCummulativeString)]; - for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - int subStringLen = strlen(e.fSubString); - if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { - // already have matching edge, go down that path - e.fChild->addOrderedNodes(name, orderedNodes); - return; - } - } - } - - // byte for terminal node size in bytes, or 0x00 if not terminal node - // teminal node (uleb128 flags, uleb128 addr) - // byte for child node count - // each child: zero terminated substring, uleb128 node offset - bool updateOffset(uint32_t& offset) { - uint32_t nodeSize = 1; // byte for length of export info - if ( fHaveExportInfo ) - nodeSize += ByteStream::uleb128_size(fFlags) + ByteStream::uleb128_size(fAddress); - - // add children - ++nodeSize; // byte for count of chidren - for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - nodeSize += strlen(e.fSubString) + 1 + ByteStream::uleb128_size(e.fChild->fTrieOffset); - } - bool result = (fTrieOffset != offset); - fTrieOffset = offset; - //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); - offset += nodeSize; - // return true if fTrieOffset was changed - return result; - } - - void appendToStream(ByteStream& out) { - if ( fHaveExportInfo ) { - // nodes with export info: size, flags, address - out.append_byte(out.uleb128_size(fFlags) + out.uleb128_size(fAddress)); - out.append_uleb128(fFlags); - out.append_uleb128(fAddress); - } - else { - // no export info - out.append_byte(0); - } - // write number of children - out.append_byte(fChildren.size()); - // write each child - for (typename std::vector<edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - edge& e = *it; - out.append_string(e.fSubString); - out.append_uleb128(e.fChild->fTrieOffset); - } - } - - }; - - - struct node fStartNode; -}; - -template <typename A> -class LocalRelocationsLinkEditAtom : public LinkEditAtom<A> -{ -public: - LocalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "local relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._local_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class SymbolTableLinkEditAtom : public LinkEditAtom<A> -{ -public: - SymbolTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "symbol table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._symbol_table"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class ExternalRelocationsLinkEditAtom : public LinkEditAtom<A> -{ -public: - ExternalRelocationsLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "external relocations"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._extern_relocs"; } - virtual void copyRawContent(uint8_t buffer[]) const; -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -struct IndirectEntry { - uint32_t indirectIndex; - uint32_t symbolIndex; -}; - - -template <typename A> -class SegmentSplitInfoContentAtom : public LinkEditAtom<A> -{ -public: - SegmentSplitInfoContentAtom(Writer<A>& writer) : LinkEditAtom<A>(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<A>::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<AtomAndOffset>& locations); - - std::vector<AtomAndOffset> fKind1Locations; - std::vector<AtomAndOffset> fKind2Locations; - std::vector<AtomAndOffset> fKind3Locations; - std::vector<AtomAndOffset> fKind4Locations; - std::vector<uint8_t> fEncodedData; - bool fCantEncode; -}; - -template <typename A> -class IndirectTableLinkEditAtom : public LinkEditAtom<A> -{ -public: - IndirectTableLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer) { } - virtual const char* getDisplayName() const { return "indirect symbol table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._indirect_syms"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - std::vector<IndirectEntry> fTable; - -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; -}; - -template <typename A> -class ModuleInfoLinkEditAtom : public LinkEditAtom<A> -{ -public: - ModuleInfoLinkEditAtom(Writer<A>& writer) : LinkEditAtom<A>(writer), fModuleNameOffset(0) { } - virtual const char* getDisplayName() const { return "module table"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._module_info"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - void setName() { fModuleNameOffset = fWriter.fStringsAtom->add("single module"); } - uint32_t getTableOfContentsFileOffset() const; - uint32_t getModuleTableFileOffset() const; - uint32_t getReferencesFileOffset() const; - uint32_t getReferencesCount() const; - -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - uint32_t fModuleNameOffset; -}; - - -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - -template <typename A> -class StringsLinkEditAtom : public LinkEditAtom<A> -{ -public: - StringsLinkEditAtom(Writer<A>& writer); - virtual const char* getDisplayName() const { return "string pool"; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "._string_pool"; } - virtual void copyRawContent(uint8_t buffer[]) const; - - int32_t add(const char* name); - int32_t addUnique(const char* name); - int32_t emptyString() { return 1; } - const char* stringForIndex(int32_t) const; - -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - enum { kBufferSize = 0x01000000 }; - typedef __gnu_cxx::hash_map<const char*, int32_t, __gnu_cxx::hash<const char*>, CStringEquals> StringToOffset; - - std::vector<char*> fFullBuffers; - char* fCurrentBuffer; - uint32_t fCurrentBufferUsed; - StringToOffset fUniqueStrings; -}; - - - -template <typename A> -class UndefinedSymbolProxyAtom : public WriterAtom<A> -{ -public: - UndefinedSymbolProxyAtom(Writer<A>& writer, const char* name) : WriterAtom<A>(writer, Segment::fgLinkEditSegment), fName(name) {} - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeGlobal; } - virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kExternalDefinition; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual uint64_t getSize() const { return 0; } - virtual const char* getSectionName() const { return "._imports"; } -private: - using WriterAtom<A>::fWriter; - typedef typename A::P P; - const char* fName; -}; - -template <typename A> -class BranchIslandAtom : public WriterAtom<A> -{ -public: - BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, - ObjectFile::Atom& finalTarget, uint32_t finalTargetOffset); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const; - virtual bool isThumb() const { return (fIslandKind == kBranchIslandToThumb2); } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kBranchIsland; } - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual const char* getSectionName() const { return "__text"; } - virtual void copyRawContent(uint8_t buffer[]) const; - uint64_t getFinalTargetAdress() const { return fFinalTarget.getAddress() + fFinalTargetOffset; } -private: - using WriterAtom<A>::fWriter; - enum IslandKind { kBranchIslandToARM, kBranchIslandToThumb2, kBranchIslandToThumb1, kBranchIslandNoPicToThumb1 }; - const char* fName; - ObjectFile::Atom& fTarget; - ObjectFile::Atom& fFinalTarget; - uint32_t fFinalTargetOffset; - IslandKind fIslandKind; -}; - -template <typename A> -class StubAtom : public WriterAtom<A> -{ -public: - StubAtom(Writer<A>& writer, ObjectFile::Atom& target, bool forLazyDylib); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStub; } - virtual uint64_t getSize() const; - virtual ObjectFile::Alignment getAlignment() const; - virtual const char* getSectionName() const { return "__symbol_stub1"; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fTarget; } - virtual uint32_t getOrdinal() const { return fSortingOrdinal; } - void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } -private: - static const char* stubName(const char* importName); - friend class LazyPointerAtom<A>; - using WriterAtom<A>::fWriter; - enum StubKind { kStubPIC, kStubNoPIC, kStubShort, kJumpTable }; - const char* fName; - ObjectFile::Atom& fTarget; - std::vector<ObjectFile::Reference*> fReferences; - bool fForLazyDylib; - StubKind fKind; - uint32_t fSortingOrdinal; -}; - - -template <typename A> -class FastStubHelperHelperAtom : public WriterAtom<A> -{ -public: - FastStubHelperHelperAtom(Writer<A>& writer); - virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "__stub_helper"; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual uint32_t getOrdinal() const { return 0; } -protected: - using WriterAtom<A>::fWriter; - std::vector<ObjectFile::Reference*> fReferences; -}; - -template <typename A> -class HybridStubHelperHelperAtom : public WriterAtom<A> -{ -public: - HybridStubHelperHelperAtom(Writer<A>& writer); - virtual const char* getName() const { return " stub helpers"; } // name sorts to start of helpers - virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableIn; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } - virtual uint64_t getSize() const; - virtual const char* getSectionName() const { return "__stub_helper"; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual uint32_t getOrdinal() const { return 0; } -protected: - using WriterAtom<A>::fWriter; - std::vector<ObjectFile::Reference*> fReferences; -}; - -template <typename A> -class StubHelperAtom : public WriterAtom<A> -{ -public: - StubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target, - LazyPointerAtom<A>& lazyPointer, bool forLazyDylib) - : WriterAtom<A>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fLazyPointerAtom(lazyPointer) { - writer.fAllSynthesizedStubHelpers.push_back(this); - } - - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kStubHelper; } - virtual const char* getSectionName() const { return "__stub_helper"; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - ObjectFile::Atom* getTarget() { return &fTarget; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(0); } - virtual uint32_t getOrdinal() const { return 1; } -protected: - static const char* stubName(const char* importName); - using WriterAtom<A>::fWriter; - const char* fName; - ObjectFile::Atom& fTarget; - LazyPointerAtom<A>& fLazyPointerAtom; - std::vector<ObjectFile::Reference*> fReferences; -}; - -template <typename A> -class ClassicStubHelperAtom : public StubHelperAtom<A> -{ -public: - ClassicStubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<A>& lazyPointer, bool forLazyDylib); - - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; -}; - - -template <typename A> -class HybridStubHelperAtom : public StubHelperAtom<A> -{ -public: - HybridStubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<A>& lazyPointer, bool forLazyDylib); - - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - static class HybridStubHelperHelperAtom<A>* fgHelperHelperAtom; -}; -template <typename A> class HybridStubHelperHelperAtom<A>* HybridStubHelperAtom<A>::fgHelperHelperAtom = NULL; - -template <typename A> -class FastStubHelperAtom : public StubHelperAtom<A> -{ -public: - FastStubHelperAtom(Writer<A>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<A>& lazyPointer, bool forLazyDylib); - virtual uint64_t getSize() const; - virtual void copyRawContent(uint8_t buffer[]) const; - static FastStubHelperHelperAtom<A>* fgHelperHelperAtom; -}; -template <typename A> FastStubHelperHelperAtom<A>* FastStubHelperAtom<A>::fgHelperHelperAtom = NULL; - - - -template <typename A> -class LazyPointerAtom : public WriterAtom<A> -{ -public: - LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target, - StubAtom<A>& stub, bool forLazyDylib); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeTranslationUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return fForLazyDylib ? ObjectFile::Atom::kLazyDylibPointer : ObjectFile::Atom::kLazyPointer; } - virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const; - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return &fExternalTarget; } - void setLazyBindingInfoOffset(uint32_t off) { fLazyBindingOffset = off; } - uint32_t getLazyBindingInfoOffset() { return fLazyBindingOffset; } - virtual uint32_t getOrdinal() const { return fSortingOrdinal; } - void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } -private: - using WriterAtom<A>::fWriter; - static const char* lazyPointerName(const char* importName); - const char* fName; - ObjectFile::Atom& fTarget; - ObjectFile::Atom& fExternalTarget; - std::vector<ObjectFile::Reference*> fReferences; - bool fForLazyDylib; - bool fCloseStub; - uint32_t fLazyBindingOffset; - uint32_t fSortingOrdinal; -}; - - -template <typename A> -class NonLazyPointerAtom : public WriterAtom<A> -{ -public: - NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target); - NonLazyPointerAtom(Writer<A>& writer, const char* targetName); - NonLazyPointerAtom(Writer<A>& writer); - virtual const char* getName() const { return fName; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual ObjectFile::Atom::ContentType getContentType() const { return ObjectFile::Atom::kNonLazyPointer; } - virtual uint64_t getSize() const { return sizeof(typename A::P::uint_t); } - virtual const char* getSectionName() const { return (fWriter.fOptions.outputKind() == Options::kKextBundle) ? "__got" : "__nl_symbol_ptr"; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual void copyRawContent(uint8_t buffer[]) const; - ObjectFile::Atom* getTarget() { return fTarget; } - virtual uint32_t getOrdinal() const { return fSortingOrdinal; } - void setSortingOrdinal(uint32_t o) { fSortingOrdinal = o; } -private: - using WriterAtom<A>::fWriter; - static const char* nonlazyPointerName(const char* importName); - const char* fName; - ObjectFile::Atom* fTarget; - std::vector<ObjectFile::Reference*> fReferences; - uint32_t fSortingOrdinal; -}; - - -template <typename A> -class ObjCInfoAtom : public WriterAtom<A> -{ -public: - ObjCInfoAtom(Writer<A>& writer, ObjectFile::Reader::ObjcConstraint objcContraint, - bool objcReplacementClasses, bool abi2override); - virtual const char* getName() const { return "objc$info"; } - virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual uint64_t getSize() const { return 8; } - virtual const char* getSectionName() const; - virtual void copyRawContent(uint8_t buffer[]) const; -private: - Segment& getInfoSegment(bool abi2override) const; - bool fAbi2override; - uint32_t fContent[2]; -}; - - -template <typename A> -class WriterReference : public ObjectFile::Reference -{ -public: - typedef typename A::ReferenceKinds Kinds; - - WriterReference(uint32_t offset, Kinds kind, ObjectFile::Atom* target, - uint32_t toOffset=0, ObjectFile::Atom* fromTarget=NULL, uint32_t fromOffset=0) - : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(target), fTargetName(target->getName()), - fTargetOffset(toOffset), fFromTarget(fromTarget), fFromTargetOffset(fromOffset) {} - WriterReference(uint32_t offset, Kinds kind, const char* targetName) - : fKind(kind), fFixUpOffsetInSrc(offset), fTarget(NULL), fTargetName(targetName), - fTargetOffset(0), fFromTarget(NULL), fFromTargetOffset(0) {} - - virtual ~WriterReference() {} - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return (fTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kUnboundByName; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const { return (fFromTarget != NULL) ? ObjectFile::Reference::kBoundDirectly : ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return (uint8_t)fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffsetInSrc; } - virtual const char* getTargetName() const { return fTargetName; } - virtual ObjectFile::Atom& getTarget() const { return *fTarget; } - virtual uint64_t getTargetOffset() const { return fTargetOffset; } - virtual ObjectFile::Atom& getFromTarget() const { return *fFromTarget; } - virtual const char* getFromTargetName() const { return fFromTarget->getName(); } - virtual void setTarget(ObjectFile::Atom& target, uint64_t offset) { fTarget = ⌖ fTargetOffset = offset; } - virtual void setFromTarget(ObjectFile::Atom& target) { fFromTarget = ⌖ } - virtual void setFromTargetName(const char* name) { } - virtual void setFromTargetOffset(uint64_t offset) { fFromTargetOffset = offset; } - virtual const char* getDescription() const { return "writer reference"; } - virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } - -private: - Kinds fKind; - uint32_t fFixUpOffsetInSrc; - ObjectFile::Atom* fTarget; - const char* fTargetName; - uint32_t fTargetOffset; - ObjectFile::Atom* fFromTarget; - uint32_t fFromTargetOffset; -}; - - -template <typename A> -const char* StubHelperAtom<A>::stubName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$stubHelper", name); - return buf; -} - -template <> -ClassicStubHelperAtom<x86_64>::ClassicStubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<x86_64>& lazyPointer, bool forLazyDylib) - : StubHelperAtom<x86_64>(writer, target, lazyPointer, forLazyDylib) -{ - fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32, &fLazyPointerAtom)); - if ( forLazyDylib ) { - if ( fWriter.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, fWriter.fDyldLazyDylibHelper)); - } - else { - if ( fWriter.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, fWriter.fDyldClassicHelperAtom)); - } -} - - -template <> -uint64_t ClassicStubHelperAtom<x86_64>::getSize() const -{ - return 12; -} - -template <> -void ClassicStubHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 - buffer[1] = 0x8D; - buffer[2] = 0x1D; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0xE9; // jmp dyld_stub_binding_helper - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x00; -} - - -template <> -FastStubHelperHelperAtom<x86_64>::FastStubHelperHelperAtom(Writer<x86_64>& writer) - : WriterAtom<x86_64>(writer, Segment::fgTextSegment) -{ - fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32, new NonLazyPointerAtom<x86_64>(writer))); - fReferences.push_back(new WriterReference<x86_64>(11, x86_64::kPCRel32, writer.fFastStubGOTAtom)); -} - -template <> -uint64_t FastStubHelperHelperAtom<x86_64>::getSize() const -{ - return 16; -} - -template <> -void FastStubHelperHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x4C; // leaq dyld_mageLoaderCache(%rip),%r11 - buffer[1] = 0x8D; - buffer[2] = 0x1D; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0x41; // pushq %r11 - buffer[8] = 0x53; - buffer[9] = 0xFF; // jmp *_fast_lazy_bind(%rip) - buffer[10] = 0x25; - buffer[11] = 0x00; - buffer[12] = 0x00; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x90; // nop -} - - -template <> -HybridStubHelperHelperAtom<x86_64>::HybridStubHelperHelperAtom(Writer<x86_64>& writer) - : WriterAtom<x86_64>(writer, Segment::fgTextSegment) -{ - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference<x86_64>(3, x86_64::kPCRel32_1, writer.fFastStubGOTAtom)); - fReferences.push_back(new WriterReference<x86_64>(13, x86_64::kPCRel32, new NonLazyPointerAtom<x86_64>(writer))); - fReferences.push_back(new WriterReference<x86_64>(21, x86_64::kPCRel32, writer.fFastStubGOTAtom)); - fReferences.push_back(new WriterReference<x86_64>(30, x86_64::kPCRel32, writer.fDyldClassicHelperAtom)); -} - -template <> -uint64_t HybridStubHelperHelperAtom<x86_64>::getSize() const -{ - return 34; -} - -template <> -void HybridStubHelperHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x48; // cmpl $0x00,_fast_lazy_bind - buffer[1] = 0x83; - buffer[2] = 0x3D; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x74; // je 16 - buffer[9] = 0x0F; - buffer[10] = 0x4C; // leaq imageCache(%rip),%r11 - buffer[11] = 0x8D; - buffer[12] = 0x1D; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x00; - buffer[16] = 0x00; - buffer[17] = 0x41; // pushq %r11 - buffer[18] = 0x53; - buffer[19] = 0xFF; // jmp *_fast_lazy_bind(%rip) - buffer[20] = 0x25; - buffer[21] = 0x00; - buffer[22] = 0x00; - buffer[23] = 0x00; - buffer[24] = 0x00; - buffer[25] = 0x48; // addq $8,%rsp - buffer[26] = 0x83; - buffer[27] = 0xC4; - buffer[28] = 0x08; - buffer[29] = 0xE9; // jmp dyld_stub_binding_helper - buffer[30] = 0x00; - buffer[31] = 0x00; - buffer[32] = 0x00; - buffer[33] = 0x00; -} - - -template <> -HybridStubHelperAtom<x86_64>::HybridStubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<x86_64>& lazyPointer, bool forLazyDylib) - : StubHelperAtom<x86_64>(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new HybridStubHelperHelperAtom<x86_64>::HybridStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference<x86_64>(8, x86_64::kPCRel32, &fLazyPointerAtom)); - fReferences.push_back(new WriterReference<x86_64>(13, x86_64::kPCRel32, fgHelperHelperAtom)); -} - -template <> -uint64_t HybridStubHelperAtom<x86_64>::getSize() const -{ - return 18; -} - -template <> -void HybridStubHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushq $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 - buffer[6] = 0x8D; - buffer[7] = 0x1D; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x00; - buffer[12] = 0xE9; // jmp helper-helper - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x00; - buffer[16] = 0x00; - buffer[17] = 0x90; // nop - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - -template <> -FastStubHelperAtom<x86_64>::FastStubHelperAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<x86_64>& lazyPointer, bool forLazyDylib) - : StubHelperAtom<x86_64>(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new FastStubHelperHelperAtom<x86_64>::FastStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference<x86_64>(6, x86_64::kPCRel32, fgHelperHelperAtom)); -} - -template <> -uint64_t FastStubHelperAtom<x86_64>::getSize() const -{ - return 10; -} - -template <> -void FastStubHelperAtom<x86_64>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushq $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xE9; // jmp helperhelper - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - -template <> -FastStubHelperHelperAtom<x86>::FastStubHelperHelperAtom(Writer<x86>& writer) - : WriterAtom<x86>(writer, Segment::fgTextSegment) -{ - fReferences.push_back(new WriterReference<x86>(1, x86::kAbsolute32, new NonLazyPointerAtom<x86>(writer))); - fReferences.push_back(new WriterReference<x86>(7, x86::kAbsolute32, writer.fFastStubGOTAtom)); -} - -template <> -uint64_t FastStubHelperHelperAtom<x86>::getSize() const -{ - return 12; -} - -template <> -void FastStubHelperHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $dyld_ImageLoaderCache - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xFF; // jmp *_fast_lazy_bind - buffer[6] = 0x25; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0x00; - buffer[11] = 0x90; // nop -} - - -template <> -FastStubHelperHelperAtom<arm>::FastStubHelperHelperAtom(Writer<arm>& writer) - : WriterAtom<arm>(writer, Segment::fgTextSegment) -{ - fReferences.push_back(new WriterReference<arm>(28, arm::kPointerDiff, new NonLazyPointerAtom<arm>(writer), 0, this, 16)); - fReferences.push_back(new WriterReference<arm>(32, arm::kPointerDiff, writer.fFastStubGOTAtom, 0, this, 28)); -} - -template <> -uint64_t FastStubHelperHelperAtom<arm>::getSize() const -{ - return 36; -} - -template <> -void FastStubHelperHelperAtom<arm>::copyRawContent(uint8_t buffer[]) const -{ - // push lazy-info-offset - OSWriteLittleInt32(&buffer[ 0], 0, 0xe52dc004); // str ip, [sp, #-4]! - // push address of dyld_mageLoaderCache - OSWriteLittleInt32(&buffer[ 4], 0, 0xe59fc010); // ldr ip, L1 - OSWriteLittleInt32(&buffer[ 8], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[12], 0, 0xe52dc004); // str ip, [sp, #-4]! - // jump through _fast_lazy_bind - OSWriteLittleInt32(&buffer[16], 0, 0xe59fc008); // ldr ip, L2 - OSWriteLittleInt32(&buffer[20], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[24], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[28], 0, 0x00000000); // L1: .long fFastStubGOTAtom - (helperhelper+16) - OSWriteLittleInt32(&buffer[32], 0, 0x00000000); // L2: .long _fast_lazy_bind - (helperhelper+28) -} - -template <> -ObjectFile::Alignment StubHelperAtom<arm>::getAlignment() const { return ObjectFile::Alignment(2); } - -template <> -FastStubHelperAtom<arm>::FastStubHelperAtom(Writer<arm>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<arm>& lazyPointer, bool forLazyDylib) - : StubHelperAtom<arm>(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new FastStubHelperHelperAtom<arm>::FastStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference<arm>(4, arm::kBranch24, fgHelperHelperAtom)); -} - -template <> -uint64_t FastStubHelperAtom<arm>::getSize() const -{ - return 12; -} - -template <> -void FastStubHelperAtom<arm>::copyRawContent(uint8_t buffer[]) const -{ - OSWriteLittleInt32(&buffer[0], 0, 0xe59fc000); // ldr ip, [pc, #0] - OSWriteLittleInt32(&buffer[4], 0, 0xea000000); // b _helperhelper - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - OSWriteLittleInt32(&buffer[8], 0, fLazyPointerAtom.getLazyBindingInfoOffset()); -} - - -template <> -HybridStubHelperHelperAtom<x86>::HybridStubHelperHelperAtom(Writer<x86>& writer) - : WriterAtom<x86>(writer, Segment::fgTextSegment) -{ - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference<x86>(2, x86::kAbsolute32, writer.fFastStubGOTAtom)); - fReferences.push_back(new WriterReference<x86>(18, x86::kPCRel32, writer.fDyldClassicHelperAtom)); - fReferences.push_back(new WriterReference<x86>(26, x86::kAbsolute32, new NonLazyPointerAtom<x86>(writer))); - fReferences.push_back(new WriterReference<x86>(32, x86::kAbsolute32, writer.fFastStubGOTAtom)); -} - -template <> -uint64_t HybridStubHelperHelperAtom<x86>::getSize() const -{ - return 36; -} - - -template <> -void HybridStubHelperHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x83; // cmpl $0x00,_fast_lazy_bind - buffer[1] = 0x3D; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - buffer[7] = 0x75; // jne 22 - buffer[8] = 0x0D; - buffer[9] = 0x89; // %eax,4(%esp) - buffer[10] = 0x44; - buffer[11] = 0x24; - buffer[12] = 0x04; - buffer[13] = 0x58; // popl %eax - buffer[14] = 0x87; // xchgl (%esp),%eax - buffer[15] = 0x04; - buffer[16] = 0x24; - buffer[17] = 0xE9; // jmpl dyld_stub_binding_helper - buffer[18] = 0x00; - buffer[19] = 0x00; - buffer[20] = 0x00; - buffer[21] = 0x00; - buffer[22] = 0x83; // addl $0x04,%esp - buffer[23] = 0xC4; - buffer[24] = 0x04; - buffer[25] = 0x68; // pushl imageloadercahce - buffer[26] = 0x00; - buffer[27] = 0x00; - buffer[28] = 0x00; - buffer[29] = 0x00; - buffer[30] = 0xFF; // jmp *_fast_lazy_bind(%rip) - buffer[31] = 0x25; - buffer[32] = 0x00; - buffer[33] = 0x00; - buffer[34] = 0x00; - buffer[35] = 0x00; -} - - -template <> -ClassicStubHelperAtom<x86>::ClassicStubHelperAtom(Writer<x86>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<x86>& lazyPointer, bool forLazyDylib) - : StubHelperAtom<x86>(writer, target, lazyPointer, forLazyDylib) -{ - fReferences.push_back(new WriterReference<x86>(1, x86::kAbsolute32, &fLazyPointerAtom)); - if ( forLazyDylib ) { - if ( fWriter.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - fReferences.push_back(new WriterReference<x86>(6, x86::kPCRel32, fWriter.fDyldLazyDylibHelper)); - } - else { - if ( fWriter.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - fReferences.push_back(new WriterReference<x86>(6, x86::kPCRel32, fWriter.fDyldClassicHelperAtom)); - } -} - -template <> -uint64_t ClassicStubHelperAtom<x86>::getSize() const -{ - return 10; -} - -template <> -void ClassicStubHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $foo$lazy_ptr - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xE9; // jmp helperhelper - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; -} - -template <> -HybridStubHelperAtom<x86>::HybridStubHelperAtom(Writer<x86>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<x86>& lazyPointer, bool forLazyDylib) - : StubHelperAtom<x86>(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new HybridStubHelperHelperAtom<x86>::HybridStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference<x86>(6, x86::kAbsolute32, &fLazyPointerAtom)); - fReferences.push_back(new WriterReference<x86>(11, x86::kPCRel32, fgHelperHelperAtom)); -} - - -template <> -uint64_t HybridStubHelperAtom<x86>::getSize() const -{ - return 16; -} - -template <> -void HybridStubHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x68; // pushl $foo$lazy_ptr - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - buffer[10] = 0xE9; // jmp dyld_hybrid_stub_binding_helper - buffer[11] = 0x00; - buffer[12] = 0x00; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x90; // nop - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - - -template <> -FastStubHelperAtom<x86>::FastStubHelperAtom(Writer<x86>& writer, ObjectFile::Atom& target, - class LazyPointerAtom<x86>& lazyPointer, bool forLazyDylib) - : StubHelperAtom<x86>(writer, target, lazyPointer, forLazyDylib) -{ - if ( fgHelperHelperAtom == NULL ) { - fgHelperHelperAtom = new FastStubHelperHelperAtom<x86>::FastStubHelperHelperAtom(fWriter); - fWriter.fAllSynthesizedStubHelpers.push_back(fgHelperHelperAtom); - } - fReferences.push_back(new WriterReference<x86>(6, x86::kPCRel32, fgHelperHelperAtom)); -} - - -template <> -uint64_t FastStubHelperAtom<x86>::getSize() const -{ - return 10; -} - -template <> -void FastStubHelperAtom<x86>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0x68; // pushl $lazy-info-offset - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0xE9; // jmp helperhelper - buffer[6] = 0x00; - buffer[7] = 0x00; - buffer[8] = 0x00; - buffer[9] = 0x00; - - // the lazy binding info is created later than this helper atom, so there - // is no Reference to update. Instead we blast the offset here. - uint32_t offset; - LittleEndian::set32(offset, fLazyPointerAtom.getLazyBindingInfoOffset()); - memcpy(&buffer[1], &offset, 4); -} - -template <typename A> -const char* LazyPointerAtom<A>::getSectionName() const -{ - if ( fCloseStub ) - return "__lazy_symbol"; - else if ( fForLazyDylib ) - return "__ld_symbol_ptr"; - else - return "__la_symbol_ptr"; -} - -// specialize lazy pointer for x86_64 to initially pointer to stub helper -template <> -LazyPointerAtom<x86_64>::LazyPointerAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, StubAtom<x86_64>& stub, bool forLazyDylib) - : WriterAtom<x86_64>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - ObjectFile::Atom* helper; - if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) { - if ( writer.fOptions.makeClassicDyldInfo() ) - // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper - if ( writer.targetRequiresWeakBinding(target) ) - helper = new ClassicStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib); - else - helper = new HybridStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib); - else { - if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - helper = ⌖ - else - helper = new FastStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib); - } - } - else { - helper = new ClassicStubHelperAtom<x86_64>(writer, target, *this, forLazyDylib); - } - fReferences.push_back(new WriterReference<x86_64>(0, x86_64::kPointer, helper)); -} - - -// specialize lazy pointer for x86 to initially pointer to stub helper -template <> -LazyPointerAtom<x86>::LazyPointerAtom(Writer<x86>& writer, ObjectFile::Atom& target, StubAtom<x86>& stub, bool forLazyDylib) - : WriterAtom<x86>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - ObjectFile::Atom* helper; - if ( writer.fOptions.makeCompressedDyldInfo() && !forLazyDylib ) { - if ( writer.fOptions.makeClassicDyldInfo() ) { - // hybrid LINKEDIT, no fast bind info for weak symbols so use traditional helper - if ( writer.targetRequiresWeakBinding(target) ) - helper = new ClassicStubHelperAtom<x86>(writer, target, *this, forLazyDylib); - else - helper = new HybridStubHelperAtom<x86>(writer, target, *this, forLazyDylib); - } - else { - if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - helper = ⌖ - else - helper = new FastStubHelperAtom<x86>(writer, target, *this, forLazyDylib); - } - } - else { - helper = new ClassicStubHelperAtom<x86>(writer, target, *this, forLazyDylib); - } - fReferences.push_back(new WriterReference<x86>(0, x86::kPointer, helper)); -} - -// specialize lazy pointer for arm to initially pointer to stub helper -template <> -LazyPointerAtom<arm>::LazyPointerAtom(Writer<arm>& writer, ObjectFile::Atom& target, StubAtom<arm>& stub, bool forLazyDylib) - : WriterAtom<arm>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - // The one instruction stubs must be close to the lazy pointers - if ( stub.fKind == StubAtom<arm>::kStubShort ) - fCloseStub = true; - - ObjectFile::Atom* helper; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - helper = writer.fDyldLazyDylibHelper; - } - else if ( writer.fOptions.makeCompressedDyldInfo() ) { - if ( target.getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - helper = ⌖ - else - helper = new FastStubHelperAtom<arm>(writer, target, *this, forLazyDylib); - } - else { - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - helper = writer.fDyldClassicHelperAtom; - } - fReferences.push_back(new WriterReference<arm>(0, arm::kPointer, helper)); -} - -template <typename A> -LazyPointerAtom<A>::LazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target, StubAtom<A>& stub, bool forLazyDylib) - : WriterAtom<A>(writer, Segment::fgDataSegment), fName(lazyPointerName(target.getName())), fTarget(target), - fExternalTarget(*stub.getTarget()), fForLazyDylib(forLazyDylib), fCloseStub(false), fLazyBindingOffset(0) -{ - if ( forLazyDylib ) - writer.fAllSynthesizedLazyDylibPointers.push_back(this); - else - writer.fAllSynthesizedLazyPointers.push_back(this); - - fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target)); -} - - - -template <typename A> -const char* LazyPointerAtom<A>::lazyPointerName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$lazy_pointer", name); - return buf; -} - -template <typename A> -void LazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - -template <typename A> -NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer, ObjectFile::Atom& target) - : WriterAtom<A>(writer, Segment::fgDataSegment), fName(nonlazyPointerName(target.getName())), fTarget(&target) -{ - writer.fAllSynthesizedNonLazyPointers.push_back(this); - fReferences.push_back(new WriterReference<A>(0, A::kPointer, &target)); -} - -template <typename A> -NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer) - : WriterAtom<A>(writer, Segment::fgDataSegment), fName("none"), fTarget(NULL) -{ - writer.fAllSynthesizedNonLazyPointers.push_back(this); -} - -template <typename A> -NonLazyPointerAtom<A>::NonLazyPointerAtom(Writer<A>& writer, const char* targetName) - : WriterAtom<A>(writer, Segment::fgDataSegment), fName(nonlazyPointerName(targetName)), fTarget(NULL) -{ - writer.fAllSynthesizedNonLazyPointers.push_back(this); - fReferences.push_back(new WriterReference<A>(0, A::kPointer, targetName)); -} - -template <typename A> -const char* NonLazyPointerAtom<A>::nonlazyPointerName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$non_lazy_pointer", name); - return buf; -} - -template <typename A> -void NonLazyPointerAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, getSize()); -} - - - - -template <> -ObjectFile::Alignment StubAtom<ppc>::getAlignment() const -{ - return 2; -} - -template <> -ObjectFile::Alignment StubAtom<ppc64>::getAlignment() const -{ - return 2; -} - -template <> -ObjectFile::Alignment StubAtom<arm>::getAlignment() const -{ - return 2; -} - -template <> -StubAtom<ppc>::StubAtom(Writer<ppc>& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom<ppc>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fForLazyDylib(forLazyDylib) -{ - writer.fAllSynthesizedStubs.push_back(this); - LazyPointerAtom<ppc>* lp; - if ( fWriter.fOptions.prebind() ) { - // for prebound ppc, lazy pointer starts out pointing to target symbol's address - // if target is a weak definition within this linkage unit or zero if in some dylib - lp = new LazyPointerAtom<ppc>(writer, target, *this, forLazyDylib); - } - else { - // for non-prebound ppc, lazy pointer starts out pointing to dyld_stub_binding_helper glue code - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - lp = new LazyPointerAtom<ppc>(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); - } - else { - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom<ppc>(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); - } - } - fKind = ( fWriter.fSlideable ? kStubPIC : kStubNoPIC ); - if ( fKind == kStubPIC ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference<ppc>(12, ppc::kPICBaseHigh16, lp, 0, this, 8)); - fReferences.push_back(new WriterReference<ppc>(20, ppc::kPICBaseLow16, lp, 0, this, 8)); - } - else { - fReferences.push_back(new WriterReference<ppc>(0, ppc::kAbsHigh16AddLow, lp)); - fReferences.push_back(new WriterReference<ppc>(4, ppc::kAbsLow16, lp)); - } -} - -template <> -StubAtom<ppc64>::StubAtom(Writer<ppc64>& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom<ppc64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), - fTarget(target), fForLazyDylib(forLazyDylib) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom<ppc64>* lp; - if ( forLazyDylib ) { - if ( writer.fDyldLazyDylibHelper == NULL ) - throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; - lp = new LazyPointerAtom<ppc64>(writer, *writer.fDyldLazyDylibHelper, *this, forLazyDylib); - } - else { - if ( writer.fDyldClassicHelperAtom == NULL ) - throw "symbol dyld_stub_binding_helper not defined (usually in crt1.o/dylib1.o/bundle1.o)"; - lp = new LazyPointerAtom<ppc64>(writer, *writer.fDyldClassicHelperAtom, *this, forLazyDylib); - } - if ( fWriter.fSlideable || ((fWriter.fPageZeroAtom != NULL) && (fWriter.fPageZeroAtom->getSize() > 4096)) ) - fKind = kStubPIC; - else - fKind = kStubNoPIC; - if ( fKind == kStubPIC ) { - // picbase is 8 bytes into atom - fReferences.push_back(new WriterReference<ppc64>(12, ppc64::kPICBaseHigh16, lp, 0, this, 8)); - fReferences.push_back(new WriterReference<ppc64>(20, ppc64::kPICBaseLow14, lp, 0, this, 8)); - } - else { - fReferences.push_back(new WriterReference<ppc64>(0, ppc64::kAbsHigh16AddLow, lp)); - fReferences.push_back(new WriterReference<ppc64>(4, ppc64::kAbsLow14, lp)); - } -} - -template <> -StubAtom<x86>::StubAtom(Writer<x86>& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom<x86>(writer, (writer.fOptions.makeCompressedDyldInfo()|| forLazyDylib) ? Segment::fgTextSegment : Segment::fgImportSegment), - fName(NULL), fTarget(target), fForLazyDylib(forLazyDylib) -{ - if ( writer.fOptions.makeCompressedDyldInfo() || forLazyDylib ) { - fKind = kStubNoPIC; - fName = stubName(target.getName()); - LazyPointerAtom<x86>* lp = new LazyPointerAtom<x86>(writer, target, *this, forLazyDylib); - fReferences.push_back(new WriterReference<x86>(2, x86::kAbsolute32, lp)); - writer.fAllSynthesizedStubs.push_back(this); - } - else { - fKind = kJumpTable; - if ( &target == NULL ) - asprintf((char**)&fName, "cache-line-crossing-stub %p", this); - else { - fName = stubName(target.getName()); - writer.fAllSynthesizedStubs.push_back(this); - } - } -} - - -template <> -StubAtom<x86_64>::StubAtom(Writer<x86_64>& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom<x86_64>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - - LazyPointerAtom<x86_64>* lp = new LazyPointerAtom<x86_64>(writer, target, *this, forLazyDylib); - fReferences.push_back(new WriterReference<x86_64>(2, x86_64::kPCRel32, lp)); -} - -template <> -StubAtom<arm>::StubAtom(Writer<arm>& writer, ObjectFile::Atom& target, bool forLazyDylib) - : WriterAtom<arm>(writer, Segment::fgTextSegment), fName(stubName(target.getName())), fTarget(target) -{ - writer.fAllSynthesizedStubs.push_back(this); - if ( (writer.fDylibSymbolCountUpperBound < 900) - && writer.fOptions.makeCompressedDyldInfo() - && (writer.fOptions.outputKind() != Options::kDynamicLibrary) - && !forLazyDylib ) { - // dylibs might have __TEXT and __DATA pulled apart to live in shared region - // if > 1000 stubs, the displacement to the lazy pointer my be > 12 bits. - fKind = kStubShort; - } - else if ( fWriter.fSlideable ) { - fKind = kStubPIC; - } - else { - fKind = kStubNoPIC; - } - LazyPointerAtom<arm>* lp = new LazyPointerAtom<arm>(writer, target, *this, forLazyDylib); - switch ( fKind ) { - case kStubPIC: - fReferences.push_back(new WriterReference<arm>(12, arm::kPointerDiff, lp, 0, this, 12)); - break; - case kStubNoPIC: - fReferences.push_back(new WriterReference<arm>(8, arm::kReadOnlyPointer, lp)); - break; - case kStubShort: - fReferences.push_back(new WriterReference<arm>(0, arm::kPointerDiff12, lp, 0, this, 8)); - break; - default: - throw "internal error"; - } -} - - - -template <typename A> -const char* StubAtom<A>::stubName(const char* name) -{ - char* buf; - asprintf(&buf, "%s$stub", name); - return buf; -} - -template <> -uint64_t StubAtom<ppc>::getSize() const -{ - - return ( (fKind == kStubPIC) ? 32 : 16 ); -} - -template <> -uint64_t StubAtom<ppc64>::getSize() const -{ - return ( (fKind == kStubPIC) ? 32 : 16 ); -} - - -template <> -uint64_t StubAtom<arm>::getSize() const -{ - switch ( fKind ) { - case kStubPIC: - return 16; - case kStubNoPIC: - return 12; - case kStubShort: - return 4; - default: - throw "internal error"; - } -} - -template <> -uint64_t StubAtom<x86>::getSize() const -{ - switch ( fKind ) { - case kStubNoPIC: - return 6; - case kJumpTable: - return 5; - default: - throw "internal error"; - } -} - -template <> -uint64_t StubAtom<x86_64>::getSize() const -{ - return 6; -} - -template <> -ObjectFile::Alignment StubAtom<x86>::getAlignment() const -{ - switch ( fKind ) { - case kStubNoPIC: - return 1; - case kJumpTable: - return 0; // special case x86 self-modifying stubs to be byte aligned - default: - throw "internal error"; - } -} - -template <> -void StubAtom<ppc64>::copyRawContent(uint8_t buffer[]) const -{ - if ( fKind == kStubPIC ) { - OSWriteBigInt32(&buffer [0], 0, 0x7c0802a6); // mflr r0 - OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase - OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 - OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) - OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 - OSWriteBigInt32(&buffer[20], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr - } - else { - OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) - OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001); // ldu r12,lo16(L_fwrite$lazy_ptr)(r11) - OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr - } -} - -template <> -void StubAtom<ppc>::copyRawContent(uint8_t buffer[]) const -{ - if ( fKind == kStubPIC ) { - OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 - OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase - OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 - OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) - OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 - OSWriteBigInt32(&buffer[20], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) - OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr - } - else { - OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_fwrite$lazy_ptr) - OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000); // lwzu r12,lo16(L_fwrite$lazy_ptr)(r11) - OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 - OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr - } -} - -template <> -void StubAtom<x86>::copyRawContent(uint8_t buffer[]) const -{ - switch ( fKind ) { - case kStubNoPIC: - buffer[0] = 0xFF; // jmp *foo$lazy_pointer - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; - break; - case kJumpTable: - if ( fWriter.fOptions.prebind() ) { - uint32_t address = this->getAddress(); - int32_t rel32 = 0 - (address+5); - buffer[0] = 0xE9; - buffer[1] = rel32 & 0xFF; - buffer[2] = (rel32 >> 8) & 0xFF; - buffer[3] = (rel32 >> 16) & 0xFF; - buffer[4] = (rel32 >> 24) & 0xFF; - } - else { - buffer[0] = 0xF4; - buffer[1] = 0xF4; - buffer[2] = 0xF4; - buffer[3] = 0xF4; - buffer[4] = 0xF4; - } - break; - default: - throw "internal error"; - } -} - -template <> -void StubAtom<x86_64>::copyRawContent(uint8_t buffer[]) const -{ - buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) - buffer[1] = 0x25; - buffer[2] = 0x00; - buffer[3] = 0x00; - buffer[4] = 0x00; - buffer[5] = 0x00; -} - -template <> -void StubAtom<arm>::copyRawContent(uint8_t buffer[]) const -{ - switch ( fKind ) { - case kStubPIC: - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 - OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) - break; - case kStubNoPIC: - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] - OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] - OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr - break; - case kStubShort: - OSWriteLittleInt32(&buffer[ 0], 0, 0xE59FF000);// ldr pc, [pc, #foo$lazy_ptr] - break; - default: - throw "internal error"; - } -} - -// x86_64 stubs are 6 bytes -template <> -ObjectFile::Alignment StubAtom<x86_64>::getAlignment() const -{ - return 1; -} - -template <> -const char* StubAtom<ppc>::getSectionName() const -{ - return ( (fKind == kStubPIC) ? "__picsymbolstub1" : "__symbol_stub1"); -} - -template <> -const char* StubAtom<ppc64>::getSectionName() const -{ - return ( (fKind == kStubPIC) ? "__picsymbolstub1" : "__symbol_stub1"); -} - -template <> -const char* StubAtom<arm>::getSectionName() const -{ - switch ( fKind ) { - case kStubPIC: - return "__picsymbolstub4"; - case kStubNoPIC: - return "__symbol_stub4"; - case kStubShort: - return "__symbolstub1"; - default: - throw "internal error"; - } -} - -template <> -const char* StubAtom<x86>::getSectionName() const -{ - switch ( fKind ) { - case kStubNoPIC: - return "__symbol_stub"; - case kJumpTable: - return "__jump_table"; - default: - throw "internal error"; - } -} - - - - -struct AtomByNameSorter -{ - bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right) - { - return (strcmp(left->getName(), right->getName()) < 0); - } -}; - -template <typename P> -struct ExternalRelocSorter -{ - bool operator()(const macho_relocation_info<P>& left, const macho_relocation_info<P>& 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 <typename A> -Writer<A>::Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries) - : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), - fAllAtoms(NULL), fStabs(NULL), fRegularDefAtomsThatOverrideADylibsWeakDef(NULL), fLoadCommandsSection(NULL), - fLoadCommandsSegment(NULL), fMachHeaderAtom(NULL), fEncryptionLoadCommand(NULL), fSegmentCommands(NULL), - fSymbolTableCommands(NULL), fHeaderPadding(NULL), fUnwindInfoAtom(NULL), - fUUIDAtom(NULL), fPadSegmentInfo(NULL), fEntryPoint( NULL), - fDyldClassicHelperAtom(NULL), fDyldCompressedHelperAtom(NULL), fDyldLazyDylibHelper(NULL), - fSectionRelocationsAtom(NULL), fCompressedRebaseInfoAtom(NULL), fCompressedBindingInfoAtom(NULL), - fCompressedWeakBindingInfoAtom(NULL), fCompressedLazyBindingInfoAtom(NULL), fCompressedExportInfoAtom(NULL), - fLocalRelocationsAtom(NULL), fExternalRelocationsAtom(NULL), - fSymbolTableAtom(NULL), fSplitCodeToDataContentAtom(NULL), fIndirectTableAtom(NULL), fModuleInfoAtom(NULL), - fStringsAtom(NULL), fPageZeroAtom(NULL), fFastStubGOTAtom(NULL), fSymbolTable(NULL), fSymbolTableCount(0), - fSymbolTableStabsCount(0), fSymbolTableLocalCount(0), fSymbolTableExportCount(0), fSymbolTableImportCount(0), - fLargestAtomSize(1), - fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false), - fCanScatter(false), fWritableSegmentPastFirst4GB(false), fNoReExportedDylibs(false), - fBiggerThanTwoGigs(false), fSlideable(false), fHasThumbBranches(false), - fFirstWritableSegment(NULL), fAnonNameIndex(1000) -{ - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - if ( fOptions.zeroPageSize() != 0 ) - fWriterSynthesizedAtoms.push_back(fPageZeroAtom = new PageZeroAtom<A>(*this)); - if ( fOptions.outputKind() == Options::kDynamicExecutable ) - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this)); - if ( fOptions.makeCompressedDyldInfo() ) - fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this)); - if ( fOptions.outputKind() == Options::kDynamicExecutable ) - fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this)); - if ( fOptions.hasCustomStack() ) - fWriterSynthesizedAtoms.push_back(new CustomStackAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new MinimalTextAtom<A>(*this)); - if ( fOptions.needsUnwindInfoSection() ) - fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this)); - if ( fOptions.makeCompressedDyldInfo() ) { - fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom<A>(*this)); - } - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this)); - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this)); - break; - case Options::kPreload: - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this)); - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this)); - case Options::kKextBundle: - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this)); - if ( fOptions.outputKind() == Options::kDynamicLibrary ) { - fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom<A>(*this)); - if ( fOptions.initFunctionName() != NULL ) - fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom<A>(*this)); - } - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this)); - if ( fOptions.makeCompressedDyldInfo() ) - fWriterSynthesizedAtoms.push_back(new DyldInfoLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this)); - if ( fOptions.sharedRegionEligible() ) - fWriterSynthesizedAtoms.push_back(new SegmentSplitInfoLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new MinimalTextAtom<A>(*this)); - if ( fOptions.needsUnwindInfoSection() ) - fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this)); - if ( fOptions.makeCompressedDyldInfo() ) { - fWriterSynthesizedAtoms.push_back(fCompressedRebaseInfoAtom = new CompressedRebaseInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedBindingInfoAtom = new CompressedBindingInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedWeakBindingInfoAtom = new CompressedWeakBindingInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedLazyBindingInfoAtom = new CompressedLazyBindingInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fCompressedExportInfoAtom = new CompressedExportInfoLinkEditAtom<A>(*this)); - } - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this)); - if ( fOptions.sharedRegionEligible() ) { - fWriterSynthesizedAtoms.push_back(fSplitCodeToDataContentAtom = new SegmentSplitInfoContentAtom<A>(*this)); - } - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this)); - if ( fOptions.makeClassicDyldInfo() ) - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this)); - if ( fOptions.outputKind() != Options::kKextBundle ) - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this)); - if ( this->needsModuleTable() ) - fWriterSynthesizedAtoms.push_back(fModuleInfoAtom = new ModuleInfoLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this)); - break; - case Options::kObjectFile: - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this)); - break; - case Options::kDyld: - fWriterSynthesizedAtoms.push_back(new DsoHandleAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fMachHeaderAtom = new MachHeaderAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fUUIDAtom = new UUIDLoadCommandAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom<A>(*this)); - if ( fOptions.needsUnwindInfoSection() ) - fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom<A>(*this)); - fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom<A>(*this)); - break; - } - - // add extra commmands - bool hasReExports = false; - uint32_t ordinal = 1; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - if ( fOptions.makeEncryptable() ) { - fEncryptionLoadCommand = new EncryptionLoadCommandsAtom<A>(*this); - fWriterSynthesizedAtoms.push_back(fEncryptionLoadCommand); - } - // fall through - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - { - // add dylib load command atoms for all dynamic libraries - const unsigned int libCount = dynamicLibraries.size(); - for (unsigned int i=0; i < libCount; ++i) { - ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i]; - //fprintf(stderr, "dynamicLibraries[%d]: reader=%p, %s, install=%s\n", i, dylibInfo.reader, dylibInfo.reader->getPath(), dylibInfo.reader->getInstallPath() ); - - if ( dylibInfo.options.fReExport ) { - hasReExports = true; - } - else { - const char* parentUmbrella = dylibInfo.reader->parentUmbrella(); - if ( (parentUmbrella != NULL) && (fOptions.outputKind() == Options::kDynamicLibrary) ) { - const char* thisIDLastSlash = strrchr(fOptions.installPath(), '/'); - if ( (thisIDLastSlash != NULL) && (strcmp(&thisIDLastSlash[1], parentUmbrella) == 0) ) - hasReExports = true; - } - } - - if ( dylibInfo.options.fWeakImport ) { - fForcedWeakImportReaders.insert(dylibInfo.reader); - } - - if ( dylibInfo.options.fBundleLoader ) { - fLibraryToOrdinal[dylibInfo.reader] = EXECUTABLE_ORDINAL; - } - else { - // see if a DylibLoadCommandsAtom has already been created for this install path - bool newDylib = true; - const char* dylibInstallPath = dylibInfo.reader->getInstallPath(); - for (unsigned int seenLib=0; seenLib < i; ++seenLib) { - ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib]; - if ( !seenDylibInfo.options.fBundleLoader ) { - const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath(); - if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) { - fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader]; - fLibraryToLoadCommand[dylibInfo.reader] = fLibraryToLoadCommand[seenDylibInfo.reader]; - fLibraryAliases[dylibInfo.reader] = seenDylibInfo.reader; - newDylib = false; - break; - } - } - } - - if ( newDylib ) { - // assign new ordinal and check for other paired load commands - fLibraryToOrdinal[dylibInfo.reader] = ordinal++; - DylibLoadCommandsAtom<A>* dyliblc = new DylibLoadCommandsAtom<A>(*this, dylibInfo); - fLibraryToLoadCommand[dylibInfo.reader] = dyliblc; - fWriterSynthesizedAtoms.push_back(dyliblc); - if ( dylibInfo.options.fReExport - && !fOptions.useSimplifiedDylibReExports() - && (fOptions.outputKind() == Options::kDynamicLibrary) ) { - // see if child has sub-framework that is this - bool isSubFramework = false; - const char* childInUmbrella = dylibInfo.reader->parentUmbrella(); - if ( childInUmbrella != NULL ) { - const char* myLeaf = strrchr(fOptions.installPath(), '/'); - if ( myLeaf != NULL ) { - if ( strcmp(childInUmbrella, &myLeaf[1]) == 0 ) - isSubFramework = true; - } - } - // LC_SUB_FRAMEWORK is in child, so do nothing in parent - if ( ! isSubFramework ) { - // this dylib also needs a sub_x load command - bool isFrameworkReExport = false; - const char* lastSlash = strrchr(dylibInstallPath, '/'); - if ( lastSlash != NULL ) { - char frameworkName[strlen(lastSlash)+20]; - sprintf(frameworkName, "/%s.framework/", &lastSlash[1]); - isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL); - } - if ( isFrameworkReExport ) { - // needs a LC_SUB_UMBRELLA command - fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom<A>(*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<A>(*this, nameStart, len)); - } - } - } - } - } - } - // add umbrella command if needed - if ( fOptions.umbrellaName() != NULL ) { - fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom<A>(*this, fOptions.umbrellaName())); - } - // add allowable client commands if used - std::vector<const char*>& allowableClients = fOptions.allowableClients(); - for (std::vector<const char*>::iterator it=allowableClients.begin(); it != allowableClients.end(); ++it) - fWriterSynthesizedAtoms.push_back(new AllowableClientLoadCommandsAtom<A>(*this, *it)); - } - break; - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kDyld: - case Options::kPreload: - case Options::kKextBundle: - break; - } - fNoReExportedDylibs = !hasReExports; - - // add any rpath load commands - for(std::vector<const char*>::const_iterator it=fOptions.rpaths().begin(); it != fOptions.rpaths().end(); ++it) { - fWriterSynthesizedAtoms.push_back(new RPathLoadCommandsAtom<A>(*this, *it)); - } - - // set up fSlideable - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - case Options::kStaticExecutable: - fSlideable = false; - break; - case Options::kDynamicExecutable: - fSlideable = fOptions.positionIndependentExecutable(); - break; - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kPreload: - case Options::kKextBundle: - fSlideable = true; - break; - } - - //fprintf(stderr, "ordinals table:\n"); - //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath()); - //} -} - -template <typename A> -Writer<A>::~Writer() -{ - if ( fFilePath != NULL ) - free((void*)fFilePath); - if ( fSymbolTable != NULL ) - delete [] fSymbolTable; -} - - -// for ppc64, -mdynamic-no-pic only works in low 2GB, so we might need to split the zeropage into two segments -template <>bool Writer<ppc64>::mightNeedPadSegment() { return (fOptions.zeroPageSize() >= 0x80000000ULL); } -template <typename A> bool Writer<A>::mightNeedPadSegment() { return false; } - - -template <typename A> -ObjectFile::Atom* Writer<A>::getUndefinedProxyAtom(const char* name) -{ - if ( fOptions.outputKind() == Options::kKextBundle ) { - return new UndefinedSymbolProxyAtom<A>(*this, name); - } - else if ( fOptions.outputKind() == Options::kObjectFile ) { - // when doing -r -exported_symbols_list, don't create proxy for a symbol - // that is supposed to be exported. We want an error instead - // <rdar://problem/5062685> ld does not report error when -r is used and exported symbols are not defined. - if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) - return NULL; - else - return new UndefinedSymbolProxyAtom<A>(*this, name); - } - else if ( (fOptions.undefinedTreatment() != Options::kUndefinedError) || fOptions.allowedUndefined(name) ) - return new UndefinedSymbolProxyAtom<A>(*this, name); - else - return NULL; -} - -template <typename A> -uint8_t Writer<A>::ordinalForLibrary(ObjectFile::Reader* lib) -{ - // flat namespace images use zero for all ordinals - if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) - return 0; - - // is an UndefinedSymbolProxyAtom - if ( lib == this ) - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) - return DYNAMIC_LOOKUP_ORDINAL; - - std::map<class ObjectFile::Reader*, uint32_t>::iterator pos = fLibraryToOrdinal.find(lib); - if ( pos != fLibraryToOrdinal.end() ) - return pos->second; - - throw "can't find ordinal for imported symbol"; -} - -template <typename A> -bool Writer<A>::targetRequiresWeakBinding(const ObjectFile::Atom& target) -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kWeakDefinition: - return true; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kTentativeDefinition: - break; - } - return false; -} - -template <typename A> -int Writer<A>::compressedOrdinalForImortedAtom(ObjectFile::Atom* target) -{ - // flat namespace images use zero for all ordinals - if ( fOptions.nameSpace() != Options::kTwoLevelNameSpace ) - return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; - - // is an UndefinedSymbolProxyAtom - ObjectFile::Reader* lib = target->getFile(); - if ( lib == this ) - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) - return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; - - std::map<class ObjectFile::Reader*, uint32_t>::iterator pos; - switch ( target->getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - pos = fLibraryToOrdinal.find(lib); - if ( pos != fLibraryToOrdinal.end() ) { - if ( pos->second == EXECUTABLE_ORDINAL ) - return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; - else - return pos->second; - } - break; - case ObjectFile::Atom::kWeakDefinition: - throw "compressedOrdinalForImortedAtom() should not have been called on a weak definition"; - case ObjectFile::Atom::kAbsoluteSymbol: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kTentativeDefinition: - return BIND_SPECIAL_DYLIB_SELF; - } - - throw "can't find ordinal for imported symbol"; -} - - -template <typename A> -ObjectFile::Atom& Writer<A>::makeObjcInfoAtom(ObjectFile::Reader::ObjcConstraint objcContraint, bool objcReplacementClasses) -{ - - return *(new ObjCInfoAtom<A>(*this, objcContraint, objcReplacementClasses, fOptions.objCABIVersion2POverride())); -} - -template <typename A> -void Writer<A>::addSynthesizedAtoms(const std::vector<class ObjectFile::Atom*>& existingAtoms, - class ObjectFile::Atom* dyldClassicHelperAtom, - class ObjectFile::Atom* dyldCompressedHelperAtom, - class ObjectFile::Atom* dyldLazyDylibHelperAtom, - bool biggerThanTwoGigs, - uint32_t dylibSymbolCount, - std::vector<class ObjectFile::Atom*>& newAtoms) -{ - fDyldClassicHelperAtom = dyldClassicHelperAtom; - fDyldCompressedHelperAtom = dyldCompressedHelperAtom; - fDyldLazyDylibHelper = dyldLazyDylibHelperAtom; - fBiggerThanTwoGigs = biggerThanTwoGigs; - fDylibSymbolCountUpperBound = dylibSymbolCount; - - // create inter-library stubs - synthesizeStubs(existingAtoms, newAtoms); -} - - -template <typename A> -uint64_t Writer<A>::write(std::vector<class ObjectFile::Atom*>& atoms, - std::vector<class ObjectFile::Reader::Stab>& stabs, - class ObjectFile::Atom* entryPointAtom, - bool createUUID, bool canScatter, ObjectFile::Reader::CpuConstraint cpuConstraint, - std::set<const class ObjectFile::Atom*>& atomsThatOverrideWeak, - bool hasExternalWeakDefinitions) -{ - fAllAtoms = &atoms; - fStabs = &stabs; - fEntryPoint = entryPointAtom; - fCanScatter = canScatter; - fCpuConstraint = cpuConstraint; - fHasWeakExports = hasExternalWeakDefinitions; // dyld needs to search this image as if it had weak exports - fRegularDefAtomsThatOverrideADylibsWeakDef = &atomsThatOverrideWeak; - - - try { - // Set for create UUID - if (createUUID) - fUUIDAtom->generate(); - - // remove uneeded dylib load commands - optimizeDylibReferences(); - - // check for mdynamic-no-pic codegen - scanForAbsoluteReferences(); - - // create table of unwind info - synthesizeUnwindInfoTable(); - - // create SegmentInfo and SectionInfo objects and assign all atoms to a section - partitionIntoSections(); - - // segment load command can now be sized and padding can be set - adjustLoadCommandsAndPadding(); - - // assign each section a file offset - assignFileOffsets(); - - // if need to add branch islands, reassign file offsets - if ( addBranchIslands() ) - assignFileOffsets(); - - // now that addresses are assigned, create unwind info - if ( fUnwindInfoAtom != NULL ) { - fUnwindInfoAtom->generate(); - // re-layout - adjustLoadCommandsAndPadding(); - assignFileOffsets(); - } - - // make spit-seg info now that all atoms exist - createSplitSegContent(); - - // build symbol table and relocations - buildLinkEdit(); - - // write map file if requested - writeMap(); - - // write everything - return writeAtoms(); - } catch (...) { - // clean up if any errors - (void)unlink(fFilePath); - throw; - } -} - -template <typename A> -void Writer<A>::buildLinkEdit() -{ - this->collectExportedAndImportedAndLocalAtoms(); - this->buildSymbolTable(); - this->buildFixups(); - this->adjustLinkEditSections(); -} - - - -template <typename A> -uint64_t Writer<A>::getAtomLoadAddress(const ObjectFile::Atom* atom) -{ - return atom->getAddress(); -// SectionInfo* info = (SectionInfo*)atom->getSection(); -// return info->getBaseAddress() + atom->getSectionOffset(); -} - -template <> -bool Writer<x86_64>::stringsNeedLabelsInObjects() -{ - return true; -} - -template <typename A> -bool Writer<A>::stringsNeedLabelsInObjects() -{ - return false; -} - -template <typename A> -const char* Writer<A>::symbolTableName(const ObjectFile::Atom* atom) -{ - static unsigned int counter = 0; - const char* name; - if ( stringsNeedLabelsInObjects() - && (atom->getContentType() == ObjectFile::Atom::kCStringType) - && (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) - asprintf((char**)&name, "LC%u", counter++); - else - name = atom->getName(); - return name; - return atom->getName(); -} - -template <typename A> -void Writer<A>::setExportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry) -{ - // set n_strx - entry->set_n_strx(this->fStringsAtom->add(this->symbolTableName(atom))); - - // set n_type - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { - entry->set_n_type(N_EXT | N_ABS); - } - else { - entry->set_n_type(N_EXT | N_SECT); - if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (fOptions.outputKind() == Options::kObjectFile) ) { - if ( fOptions.keepPrivateExterns() ) - entry->set_n_type(N_EXT | N_SECT | N_PEXT); - } - } - - // set n_sect (section number of implementation ) - uint8_t sectionIndex = atom->getSection()->getIndex(); - entry->set_n_sect(sectionIndex); - - // the __mh_execute_header is magic and must be an absolute symbol - if ( (sectionIndex==0) - && (fOptions.outputKind() == Options::kDynamicExecutable) - && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip )) - entry->set_n_type(N_EXT | N_ABS); - - // set n_desc - uint16_t desc = 0; - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) - desc |= REFERENCED_DYNAMICALLY; - if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) - desc |= N_NO_DEAD_STRIP; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - desc |= N_WEAK_DEF; - fHasWeakExports = true; - } - entry->set_n_desc(desc); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - entry->set_n_value(atom->getSectionOffset()); - else - entry->set_n_value(this->getAtomLoadAddress(atom)); -} - -template <typename A> -void Writer<A>::setImportNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry) -{ - // set n_strx - entry->set_n_strx(this->fStringsAtom->add(atom->getName())); - - // set n_type - if ( fOptions.outputKind() == Options::kObjectFile ) { - if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) - entry->set_n_type(N_UNDF | N_EXT | N_PEXT); - else - entry->set_n_type(N_UNDF | N_EXT); - } - else { - if ( fOptions.prebind() ) - entry->set_n_type(N_PBUD | N_EXT); - else - entry->set_n_type(N_UNDF | N_EXT); - } - - // set n_sect - entry->set_n_sect(0); - - uint16_t desc = 0; - if ( fOptions.outputKind() != Options::kObjectFile ) { - // set n_desc ( high byte is library ordinal, low byte is reference type ) - std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(atom); - if ( pos != fStubsMap.end() || ( strncmp(atom->getName(), ".objc_class_name_", 17) == 0) ) - desc = REFERENCE_FLAG_UNDEFINED_LAZY; - else - desc = REFERENCE_FLAG_UNDEFINED_NON_LAZY; - try { - uint8_t ordinal = this->ordinalForLibrary(atom->getFile()); - //fprintf(stderr, "ordinal=%u from reader=%p for symbol=%s\n", ordinal, atom->getFile(), atom->getName()); - SET_LIBRARY_ORDINAL(desc, ordinal); - } - catch (const char* msg) { - throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else if ( atom->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) { - uint8_t align = atom->getAlignment().powerOf2; - // always record custom alignment of common symbols to match what compiler does - SET_COMM_ALIGN(desc, align); - } - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAndNeverStrip ) - desc |= REFERENCED_DYNAMICALLY; - if ( ( fOptions.outputKind() != Options::kObjectFile) && (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - desc |= N_REF_TO_WEAK; - fReferencesWeakImports = true; - } - // set weak_import attribute - if ( fWeakImportMap[atom] ) - desc |= N_WEAK_REF; - entry->set_n_desc(desc); - - // set n_value, zero for import proxy and size for tentative definition - entry->set_n_value(atom->getSize()); -} - - -template <typename A> -void Writer<A>::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist<P>* entry) -{ - // set n_strx - const char* symbolName = this->symbolTableName(atom); - char anonName[32]; - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.keepLocalSymbol(symbolName) ) { - if ( stringsNeedLabelsInObjects() && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { - // don't use 'l' labels for x86_64 strings - // <rdar://problem/6605499> x86_64 obj-c runtime confused when static lib is stripped - } - else { - sprintf(anonName, "l%u", fAnonNameIndex++); - symbolName = anonName; - } - } - entry->set_n_strx(this->fStringsAtom->add(symbolName)); - - // set n_type - uint8_t type = N_SECT; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - type = N_ABS; - if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit ) - type |= N_PEXT; - entry->set_n_type(type); - - // set n_sect (section number of implementation ) - uint8_t sectIndex = atom->getSection()->getIndex(); - if ( sectIndex == 0 ) { - // see <mach-o/ldsyms.h> synthesized lable for mach_header needs special section number... - if ( strcmp(atom->getSectionName(), "._mach_header") == 0 ) - sectIndex = 1; - } - entry->set_n_sect(sectIndex); - - // set n_desc - uint16_t desc = 0; - if ( atom->dontDeadStrip() && (fOptions.outputKind() == Options::kObjectFile) ) - desc |= N_NO_DEAD_STRIP; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - desc |= N_WEAK_DEF; - if ( atom->isThumb() ) - desc |= N_ARM_THUMB_DEF; - entry->set_n_desc(desc); - - // set n_value ( address this symbol will be at if this executable is loaded at it preferred address ) - if ( atom->getDefinitionKind() == ObjectFile::Atom::kAbsoluteSymbol ) - entry->set_n_value(atom->getSectionOffset()); - else - entry->set_n_value(this->getAtomLoadAddress(atom)); -} - - -template <typename A> -void Writer<A>::addLocalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) -{ - macho_nlist<P> 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 <typename A> -void Writer<A>::addGlobalLabel(ObjectFile::Atom& atom, uint32_t offsetInAtom, const char* name) -{ - macho_nlist<P> 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 <typename A> -void Writer<A>::setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count) -{ - macho_nlist<P>* entry = &fSymbolTable[startIndex]; - for (uint32_t i=0; i < count; ++i, ++entry) { - ObjectFile::Atom* atom = atoms[i]; - if ( &atoms == &fExportedAtoms ) { - this->setExportNlist(atom, entry); - } - else if ( &atoms == &fImportedAtoms ) { - this->setImportNlist(atom, entry); - } - else { - this->setLocalNlist(atom, entry); - } - } -} - -template <typename A> -void Writer<A>::copyNlistRange(const std::vector<macho_nlist<P> >& entries, uint32_t startIndex) -{ - for ( typename std::vector<macho_nlist<P> >::const_iterator it = entries.begin(); it != entries.end(); ++it) - fSymbolTable[startIndex++] = *it; -} - - -template <typename A> -struct NListNameSorter -{ - NListNameSorter(StringsLinkEditAtom<A>* pool) : fStringPool(pool) {} - - bool operator()(const macho_nlist<typename A::P>& left, const macho_nlist<typename A::P>& right) - { - return (strcmp(fStringPool->stringForIndex(left.n_strx()), fStringPool->stringForIndex(right.n_strx())) < 0); - } -private: - StringsLinkEditAtom<A>* fStringPool; -}; - - -template <typename A> -void Writer<A>::buildSymbolTable() -{ - fSymbolTableStabsStartIndex = 0; - fSymbolTableStabsCount = fStabs->size(); - fSymbolTableLocalStartIndex = fSymbolTableStabsStartIndex + fSymbolTableStabsCount; - fSymbolTableLocalCount = fLocalSymbolAtoms.size() + fLocalExtraLabels.size(); - fSymbolTableExportStartIndex = fSymbolTableLocalStartIndex + fSymbolTableLocalCount; - fSymbolTableExportCount = fExportedAtoms.size() + fGlobalExtraLabels.size(); - fSymbolTableImportStartIndex = fSymbolTableExportStartIndex + fSymbolTableExportCount; - fSymbolTableImportCount = fImportedAtoms.size(); - - // allocate symbol table - fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount; - fSymbolTable = new macho_nlist<P>[fSymbolTableCount]; - - // fill in symbol table and string pool (do stabs last so strings are at end of pool) - setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex, fLocalSymbolAtoms.size()); - if ( fLocalExtraLabels.size() != 0 ) - copyNlistRange(fLocalExtraLabels, fSymbolTableLocalStartIndex+fLocalSymbolAtoms.size()); - setNlistRange(fExportedAtoms, fSymbolTableExportStartIndex, fExportedAtoms.size()); - if ( fGlobalExtraLabels.size() != 0 ) { - copyNlistRange(fGlobalExtraLabels, fSymbolTableExportStartIndex+fExportedAtoms.size()); - // re-sort combined range - std::sort( &fSymbolTable[fSymbolTableExportStartIndex], - &fSymbolTable[fSymbolTableExportStartIndex+fSymbolTableExportCount], - NListNameSorter<A>(fStringsAtom) ); - } - setNlistRange(fImportedAtoms, fSymbolTableImportStartIndex, fSymbolTableImportCount); - addStabs(fSymbolTableStabsStartIndex); - - // set up module table - if ( fModuleInfoAtom != NULL ) - fModuleInfoAtom->setName(); - - // create atom to symbol index map - // imports - int i = 0; - for(std::vector<ObjectFile::Atom*>::iterator it=fImportedAtoms.begin(); it != fImportedAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableImportStartIndex; - ++i; - } - // locals - i = 0; - for(std::vector<ObjectFile::Atom*>::iterator it=fLocalSymbolAtoms.begin(); it != fLocalSymbolAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableLocalStartIndex; - ++i; - } - // exports - i = 0; - for(std::vector<ObjectFile::Atom*>::iterator it=fExportedAtoms.begin(); it != fExportedAtoms.end(); ++it) { - fAtomToSymbolIndex[*it] = i + fSymbolTableExportStartIndex; - ++i; - } - -} - - - -template <typename A> -bool Writer<A>::shouldExport(const ObjectFile::Atom& atom) const -{ - switch ( atom.getSymbolTableInclusion() ) { - case ObjectFile::Atom::kSymbolTableNotIn: - return false; - case ObjectFile::Atom::kSymbolTableInAndNeverStrip: - return true; - case ObjectFile::Atom::kSymbolTableInAsAbsolute: - case ObjectFile::Atom::kSymbolTableIn: - switch ( atom.getScope() ) { - case ObjectFile::Atom::scopeGlobal: - return true; - case ObjectFile::Atom::scopeLinkageUnit: - return ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ); - default: - return false; - } - break; - } - return false; -} - -template <typename A> -void Writer<A>::collectExportedAndImportedAndLocalAtoms() -{ - const int atomCount = fAllAtoms->size(); - // guess at sizes of each bucket to minimize re-allocations - fImportedAtoms.reserve(100); - fExportedAtoms.reserve(atomCount/2); - fLocalSymbolAtoms.reserve(atomCount); - - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections; - for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - std::vector<ObjectFile::Atom*>& sectionAtoms = (*secit)->fAtoms; - for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - // only named atoms go in symbol table - if ( atom->getName() != NULL ) { - // put atom into correct bucket: imports, exports, locals - //fprintf(stderr, "collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName()); - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - fImportedAtoms.push_back(atom); - break; - case ObjectFile::Atom::kTentativeDefinition: - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fMakeTentativeDefinitionsReal ) { - fImportedAtoms.push_back(atom); - break; - } - // else fall into - case ObjectFile::Atom::kWeakDefinition: - if ( stringsNeedLabelsInObjects() - && (fOptions.outputKind() == Options::kObjectFile) - && (atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn) - && (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) - && (atom->getContentType() == ObjectFile::Atom::kCStringType) ) { - fLocalSymbolAtoms.push_back(atom); - break; - } - // else fall into - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - if ( this->shouldExport(*atom) ) - fExportedAtoms.push_back(atom); - else if ( (atom->getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) - && ((fOptions.outputKind() == Options::kObjectFile) || fOptions.keepLocalSymbol(atom->getName())) ) - fLocalSymbolAtoms.push_back(atom); - break; - } - } - // when geneating a .o file, dtrace static probes become local labels - if ( (fOptions.outputKind() == Options::kObjectFile) && !fOptions.readerOptions().fForStatic ) { - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addLocalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); - } - } - } - // when linking kernel, old style dtrace static probes become global labels - else if ( fOptions.readerOptions().fForStatic ) { - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == A::kDtraceProbe ) { - // dtrace probe points to be add back into generated .o file - this->addGlobalLabel(*atom, ref->getFixUpOffset(), ref->getTargetName()); - } - } - } - } - } - } - - // sort exported atoms by name - std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), AtomByNameSorter()); - // sort imported atoms by name (not required by runtime, but helps make generated files binary diffable) - std::sort(fImportedAtoms.begin(), fImportedAtoms.end(), AtomByNameSorter()); -} - - -template <typename A> -uint64_t Writer<A>::valueForStab(const ObjectFile::Reader::Stab& stab) -{ - switch ( stab.type ) { - case N_FUN: - if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { - // end of function N_FUN has size - return stab.atom->getSize(); - } - else { - // start of function N_FUN has address - return getAtomLoadAddress(stab.atom); - } - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - if ( stab.atom == NULL ) - // some weird assembly files have slines not associated with a function - return stab.value; - else - // all these stab types need their value changed from an offset in the atom to an address - return getAtomLoadAddress(stab.atom) + stab.value; - case N_STSYM: - case N_LCSYM: - case N_BNSYM: - // all these need address of atom - return getAtomLoadAddress(stab.atom);; - case N_ENSYM: - return stab.atom->getSize(); - case N_SO: - if ( stab.atom == NULL ) { - return 0; - } - else { - if ( (stab.string == NULL) || (strlen(stab.string) == 0) ) { - // end of translation unit N_SO has address of end of last atom - return getAtomLoadAddress(stab.atom) + stab.atom->getSize(); - } - else { - // start of translation unit N_SO has address of end of first atom - return getAtomLoadAddress(stab.atom); - } - } - break; - default: - return stab.value; - } -} - -template <typename A> -uint32_t Writer<A>::stringOffsetForStab(const ObjectFile::Reader::Stab& stab) -{ - switch (stab.type) { - case N_SO: - if ( (stab.string == NULL) || stab.string[0] == '\0' ) { - return this->fStringsAtom->emptyString(); - break; - } - // fall into uniquing case - case N_SOL: - case N_BINCL: - case N_EXCL: - return this->fStringsAtom->addUnique(stab.string); - break; - default: - if ( stab.string == NULL ) - return 0; - else if ( stab.string[0] == '\0' ) - return this->fStringsAtom->emptyString(); - else - return this->fStringsAtom->add(stab.string); - } - return 0; -} - -template <typename A> -uint8_t Writer<A>::sectionIndexForStab(const ObjectFile::Reader::Stab& stab) -{ - // in FUN stabs, n_sect field is 0 for start FUN and 1 for end FUN - if ( stab.type == N_FUN ) - return stab.other; - else if ( stab.atom != NULL ) - return stab.atom->getSection()->getIndex(); - else - return stab.other; -} - -template <typename A> -void Writer<A>::addStabs(uint32_t startIndex) -{ - macho_nlist<P>* entry = &fSymbolTable[startIndex]; - for(std::vector<ObjectFile::Reader::Stab>::iterator it = fStabs->begin(); it != fStabs->end(); ++it, ++entry) { - const ObjectFile::Reader::Stab& stab = *it; - entry->set_n_type(stab.type); - entry->set_n_sect(sectionIndexForStab(stab)); - entry->set_n_desc(stab.desc); - entry->set_n_value(valueForStab(stab)); - entry->set_n_strx(stringOffsetForStab(stab)); - } -} - - - -template <typename A> -uint32_t Writer<A>::symbolIndex(ObjectFile::Atom& atom) -{ - std::map<ObjectFile::Atom*, uint32_t>::iterator pos = fAtomToSymbolIndex.find(&atom); - if ( pos != fAtomToSymbolIndex.end() ) - return pos->second; - throwf("atom not found in symbolIndex(%s) for %s", atom.getDisplayName(), atom.getFile()->getPath()); -} - - -template <> -bool Writer<x86_64>::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 <typename A> -bool Writer<A>::makesExternalRelocatableReference(ObjectFile::Atom& target) const -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kTentativeDefinition: - if ( fOptions.readerOptions().fMakeTentativeDefinitionsReal ) - return false; - else - return (target.getScope() != ObjectFile::Atom::scopeTranslationUnit); - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - return shouldExport(target); - } - return false; -} - -template <typename A> -void Writer<A>::buildFixups() -{ - if ( fOptions.outputKind() == Options::kObjectFile ) { - this->buildObjectFileFixups(); - } - else { - if ( fOptions.keepRelocations() ) - this->buildObjectFileFixups(); - this->buildExecutableFixups(); - } -} - -template <> -uint32_t Writer<x86_64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool external = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = external ? this->symbolIndex(target) : target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info<P> reloc1; - macho_relocation_info<P> reloc2; - x86_64::ReferenceKinds kind = (x86_64::ReferenceKinds)ref->getKind(); - - switch ( kind ) { - case x86_64::kNoFixUp: - case x86_64::kGOTNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - return 0; - - case x86_64::kPointer: - case x86_64::kPointerWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(3); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPointer32: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPointerDiff32: - case x86_64::kPointerDiff: - { - ObjectFile::Atom& fromTarget = ref->getFromTarget(); - bool fromExternal = (fromTarget.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn); - uint32_t fromSymbolIndex = fromExternal ? this->symbolIndex(fromTarget) : fromTarget.getSection()->getIndex(); - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_UNSIGNED); - reloc2.set_r_address(address); - reloc2.set_r_symbolnum(fromSymbolIndex); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(kind==x86_64::kPointerDiff32 ? 2 : 3); - reloc2.set_r_extern(fromExternal); - reloc2.set_r_type(X86_64_RELOC_SUBTRACTOR); - fSectionRelocs.push_back(reloc1); - fSectionRelocs.push_back(reloc2); - return 2; - } - - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kDtraceProbeSite: - case x86_64::kDtraceIsEnabledSite: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_BRANCH); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_1: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_1); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_2: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_2); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32_4: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_SIGNED_4); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kBranchPCRel8: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(0); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_BRANCH); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_GOT); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(symbolIndex); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(external); - reloc1.set_r_type(X86_64_RELOC_GOT_LOAD); - fSectionRelocs.push_back(reloc1); - return 1; - - case x86_64::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - - case x86_64::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - - case x86_64::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // generates no relocs - return 0; - } - return 0; -} - - -template <> -uint32_t Writer<x86>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info<P> reloc1; - macho_relocation_info<P> reloc2; - macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1; - macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2; - x86::ReferenceKinds kind = (x86::ReferenceKinds)ref->getKind(); - - if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) - warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", - target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); - - - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - return 0; - - case x86::kPointer: - case x86::kPointerWeakImport: - case x86::kAbsolute32: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case x86::kPointerDiff16: - case x86::kPointerDiff: - { - //pint_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - //fprintf(stderr, "addObjectRelocs(): refFromTarget=%s, refTarget=%s, refFromTargetAddr=0x%llX, refFromTargetOffset=0x%llX\n", - // ref->getFromTarget().getDisplayName(), ref->getTarget().getDisplayName(), - // ref->getFromTarget().getAddress(), ref->getFromTargetOffset()); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(GENERIC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length( (kind==x86::kPointerDiff) ? 2 : 1 ); - sreloc2->set_r_type(GENERIC_RELOC_PAIR); - sreloc2->set_r_address(0); - if ( &ref->getFromTarget() == atom ) - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - else - sreloc2->set_r_value(ref->getFromTarget().getAddress()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case x86::kPCRel32WeakImport: - case x86::kPCRel32: - case x86::kPCRel16: - case x86::kPCRel8: - case x86::kDtraceProbeSite: - case x86::kDtraceIsEnabledSite: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length( (kind==x86::kPCRel8) ? 0 : ((kind==x86::kPCRel16) ? 1 : 2) ); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case x86::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - - case x86::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - - case x86::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - - case x86::kDtraceTypeReference: - case x86::kDtraceProbe: - // generates no relocs - return 0; - - } - return 0; -} - -template <> -uint32_t Writer<arm>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info<P> reloc1; - macho_relocation_info<P> reloc2; - macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1; - macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2; - arm::ReferenceKinds kind = (arm::ReferenceKinds)ref->getKind(); - - if ( !isExtern && (sectionNum == 0) && (target.getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) - warning("section index == 0 for %s (kind=%d, scope=%d, inclusion=%d) in %s", - target.getDisplayName(), target.getDefinitionKind(), target.getScope(), target.getSymbolTableInclusion(), target.getFile()->getPath()); - - - switch ( kind ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - return 0; - - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kPointerDiff: - { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(ARM_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(ARM_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - if ( ref->getTargetOffset() >= target.getSize() ) - sreloc1->set_r_value(target.getAddress()); - else - sreloc1->set_r_value(target.getAddress()+ref->getTargetOffset()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(ARM_RELOC_PAIR); - sreloc2->set_r_address(0); - if ( &ref->getFromTarget() == atom ) { - unsigned int pcBaseOffset = atom->isThumb() ? 4 : 8; - if ( (ref->getFromTargetOffset() > pcBaseOffset) && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()-pcBaseOffset); - } - else - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - } - else - sreloc2->set_r_value(ref->getFromTarget().getAddress()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case arm::kBranch24WeakImport: - case arm::kBranch24: - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc is target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_RELOC_BR24); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - if ( !isExtern && (ref->getTargetOffset() != 0) ) { - // use scattered reloc if target offset is non-zero - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(ARM_THUMB_RELOC_BR22); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - reloc1.set_r_symbolnum(isExtern ? symbolIndex : sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(ARM_THUMB_RELOC_BR22); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case arm::kPointerDiff12: - throw "internal error. no reloc for 12-bit pointer diffs"; - - case arm::kDtraceTypeReference: - case arm::kDtraceProbe: - // generates no relocs - return 0; - - } - return 0; -} - -template <> uint64_t Writer<ppc>::maxAddress() { return 0xFFFFFFFFULL; } -template <> uint64_t Writer<ppc64>::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } -template <> uint64_t Writer<x86>::maxAddress() { return 0xFFFFFFFFULL; } -template <> uint64_t Writer<x86_64>::maxAddress() { return 0xFFFFFFFFFFFFFFFFULL; } -template <> uint64_t Writer<arm>::maxAddress() { return 0xFFFFFFFFULL; } - -template <> -uint8_t Writer<ppc>::getRelocPointerSize() -{ - return 2; -} - -template <> -uint8_t Writer<ppc64>::getRelocPointerSize() -{ - return 3; -} - -template <> -uint32_t Writer<ppc>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - return addObjectRelocs_powerpc(atom, ref); -} - -template <> -uint32_t Writer<ppc64>::addObjectRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - return addObjectRelocs_powerpc(atom, ref); -} - -// -// addObjectRelocs<ppc> and addObjectRelocs<ppc64> are almost exactly the same, so -// they use a common addObjectRelocs_powerpc() method. -// -template <typename A> -uint32_t Writer<A>::addObjectRelocs_powerpc(ObjectFile::Atom* atom, ObjectFile::Reference* ref) -{ - ObjectFile::Atom& target = ref->getTarget(); - bool isExtern = this->makesExternalRelocatableReference(target); - uint32_t symbolIndex = 0; - if ( isExtern ) - symbolIndex = this->symbolIndex(target); - uint32_t sectionNum = target.getSection()->getIndex(); - uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset(); - macho_relocation_info<P> reloc1; - macho_relocation_info<P> reloc2; - macho_scattered_relocation_info<P>* sreloc1 = (macho_scattered_relocation_info<P>*)&reloc1; - macho_scattered_relocation_info<P>* sreloc2 = (macho_scattered_relocation_info<P>*)&reloc2; - typename A::ReferenceKinds kind = (typename A::ReferenceKinds)ref->getKind(); - - switch ( kind ) { - case A::kNoFixUp: - case A::kFollowOn: - case A::kGroupSubordinate: - return 0; - - case A::kPointer: - case A::kPointerWeakImport: - if ( !isExtern && (ref->getTargetOffset() >= target.getSize()) ) { - // use scattered reloc is target offset is outside target - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(getRelocPointerSize()); - sreloc1->set_r_type(GENERIC_RELOC_VANILLA); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - else { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(getRelocPointerSize()); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kPointerDiff16: - case A::kPointerDiff32: - case A::kPointerDiff64: - { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length( (kind == A::kPointerDiff32) ? 2 : ((kind == A::kPointerDiff64) ? 3 : 1)); - if ( ref->getTarget().getScope() == ObjectFile::Atom::scopeTranslationUnit ) - sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); - else - sreloc1->set_r_type(PPC_RELOC_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(sreloc1->r_length()); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(0); - sreloc2->set_r_value(ref->getFromTarget().getAddress()+ref->getFromTargetOffset()); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kBranch24WeakImport: - case A::kBranch24: - case A::kDtraceProbeSite: - case A::kDtraceIsEnabledSite: - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_type(PPC_RELOC_BR24); - reloc1.set_r_extern(isExtern); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR24); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kBranch14: - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(true); - reloc1.set_r_length(2); - reloc1.set_r_type(PPC_RELOC_BR14); - reloc1.set_r_extern(isExtern); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(true); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_BR14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - fSectionRelocs.push_back(reloc1); - return 1; - - case A::kPICBaseLow16: - case A::kPICBaseLow14: - { - pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(kind == A::kPICBaseLow16 ? PPC_RELOC_LO16_SECTDIFF : PPC_RELOC_LO14_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address(((toAddr-fromAddr) >> 16) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kPICBaseHigh16: - { - pint_t fromAddr = atom->getAddress() + ref->getFromTargetOffset(); - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - sreloc2->set_r_scattered(true); - sreloc2->set_r_pcrel(false); - sreloc2->set_r_length(2); - sreloc2->set_r_type(PPC_RELOC_PAIR); - sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF); - sreloc2->set_r_value(fromAddr); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsLow14: - case A::kAbsLow16: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(kind==A::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() >> 16); - else - reloc2.set_r_address(toAddr >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsHigh16: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(PPC_RELOC_HI16); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HI16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); - else - reloc2.set_r_address(toAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kAbsHigh16AddLow: - { - pint_t toAddr = target.getAddress() + ref->getTargetOffset(); - uint32_t overflow = 0; - if ( (toAddr & 0x00008000) != 0 ) - overflow = 0x10000; - if ( (ref->getTargetOffset() == 0) || isExtern ) { - reloc1.set_r_address(address); - if ( isExtern ) - reloc1.set_r_symbolnum(symbolIndex); - else - reloc1.set_r_symbolnum(sectionNum); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(PPC_RELOC_HA16); - } - else { - sreloc1->set_r_scattered(true); - sreloc1->set_r_pcrel(false); - sreloc1->set_r_length(2); - sreloc1->set_r_type(PPC_RELOC_HA16); - sreloc1->set_r_address(address); - sreloc1->set_r_value(target.getAddress()); - } - if ( isExtern ) - reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); - else - reloc2.set_r_address(toAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fSectionRelocs.push_back(reloc2); - fSectionRelocs.push_back(reloc1); - return 2; - } - - case A::kDtraceTypeReference: - case A::kDtraceProbe: - // generates no relocs - return 0; - } - return 0; -} - - - -// -// There are cases when an entry in the indirect symbol table is the magic value -// INDIRECT_SYMBOL_LOCAL instead of being a symbol index. When that happens -// the content of the corresponding part of the __nl_symbol_pointer section -// must also change. -// -template <typename A> -bool Writer<A>::indirectSymbolInRelocatableIsLocal(const ObjectFile::Reference* ref) const -{ - // cannot use INDIRECT_SYMBOL_LOCAL to tentative definitions in object files - // because tentative defs don't have addresses - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition ) - return false; - - // must use INDIRECT_SYMBOL_LOCAL if there is an addend - if ( ref->getTargetOffset() != 0 ) - return true; - - // don't use INDIRECT_SYMBOL_LOCAL for external symbols - return ! this->shouldExport(ref->getTarget()); -} - - -template <typename A> -void Writer<A>::buildObjectFileFixups() -{ - uint32_t relocIndex = 0; - std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector<SectionInfo*>& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - //fprintf(stderr, "buildObjectFileFixups(): starting section %s\n", curSection->fSectionName); - std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms; - if ( ! curSection->fAllZeroFill ) { - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers - || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) - curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); - curSection->fRelocOffset = relocIndex; - const int atomCount = sectionAtoms.size(); - for (int k=0; k < atomCount; ++k) { - ObjectFile::Atom* atom = sectionAtoms[k]; - //fprintf(stderr, "buildObjectFileFixups(): atom %s has %lu references\n", atom->getDisplayName(), atom->getReferences().size()); - std::vector<ObjectFile::Reference*>& refs = atom->getReferences(); - const int refCount = refs.size(); - for (int l=0; l < refCount; ++l) { - ObjectFile::Reference* ref = refs[l]; - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers - || curSection->fAllLazyDylibPointers || curSection->fAllStubs ) { - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / atom->getSize(); - uint32_t undefinedSymbolIndex; - if ( curSection->fAllStubs ) { - ObjectFile::Atom& stubTarget =ref->getTarget(); - ObjectFile::Atom& stubTargetTarget = stubTarget.getReferences()[0]->getTarget(); - undefinedSymbolIndex = this->symbolIndex(stubTargetTarget); - //fprintf(stderr, "stub %s ==> %s ==> %s ==> index:%u\n", atom->getDisplayName(), stubTarget.getDisplayName(), stubTargetTarget.getDisplayName(), undefinedSymbolIndex); - } - else if ( curSection->fAllNonLazyPointers) { - // only use INDIRECT_SYMBOL_LOCAL in non-lazy-pointers for atoms that won't be in symbol table or have an addend - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - else - undefinedSymbolIndex = this->symbolIndex(ref->getTarget()); - } - else { - // should never get here, fAllLazyPointers not used in generated .o files - undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - } - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //printf("fIndirectTableAtom->fTable.add(sectionIndex=%u, indirectTableIndex=%u => %u), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, atom->getSize()); - fIndirectTableAtom->fTable.push_back(entry); - if ( curSection->fAllLazyPointers ) { - ObjectFile::Atom& target = ref->getTarget(); - ObjectFile::Atom& fromTarget = ref->getFromTarget(); - if ( &fromTarget == NULL ) { - warning("lazy pointer %s missing initial binding", atom->getDisplayName()); - } - else { - bool isExtern = ( ((target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition)) - && (target.getSymbolTableInclusion() != ObjectFile::Atom::kSymbolTableNotIn) ); - macho_relocation_info<P> reloc1; - reloc1.set_r_address(atom->getSectionOffset()); - reloc1.set_r_symbolnum(isExtern ? this->symbolIndex(target) : target.getSection()->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(); - reloc1.set_r_extern(isExtern); - reloc1.set_r_type(GENERIC_RELOC_VANILLA); - fSectionRelocs.push_back(reloc1); - ++relocIndex; - } - } - else if ( curSection->fAllStubs ) { - relocIndex += this->addObjectRelocs(atom, ref); - } - } - else if ( (ref->getKind() != A::kNoFixUp) && (ref->getTargetBinding() != ObjectFile::Reference::kDontBind) ) { - relocIndex += this->addObjectRelocs(atom, ref); - } - } - } - curSection->fRelocCount = relocIndex - curSection->fRelocOffset; - } - } - } - - // reverse the relocs - std::reverse(fSectionRelocs.begin(), fSectionRelocs.end()); - - // now reverse section reloc offsets - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector<SectionInfo*>& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; - } - } - -} - - -template <> -uint64_t Writer<x86_64>::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - uint64_t result; - if ( fOptions.outputKind() == Options::kKextBundle ) { - // for x86_64 kext bundles, the r_address field in relocs - // is the offset from the start address of the first segment - result = address - fSegmentInfos[0]->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - throwf("kext bundle too large: address can't fit in 31-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - // for x86_64, the r_address field in relocs for final linked images - // is the offset from the start address of the first writable segment - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) - throwf("text relocs not supported for x86_64 in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - else - throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - return result; -} - - -template <> -bool Writer<ppc>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) - return true; - } - return false; -} - - -template <> -bool Writer<ppc64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) - return true; - } - return false; -} - -template <> -bool Writer<x86>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // illegal in dylibs/bundles, until we support TEXT relocs - return fSlideable; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // illegal until we support TEXT relocs - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - // absolute symbbols only allowed in static executables - return ( fOptions.outputKind() != Options::kStaticExecutable); - } - } - return false; -} - -template <> -bool Writer<x86_64>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - if ( fOptions.outputKind() == Options::kKextBundle ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // true means we need a TEXT relocs - switch ( ref.getKind() ) { - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - return true; - } - break; - } - } - return false; -} - -template <> -bool Writer<arm>::illegalRelocInFinalLinkedImage(const ObjectFile::Reference& ref) -{ - switch ( fOptions.outputKind()) { - case Options::kStaticExecutable: - case Options::kPreload: - // all relocations allowed in static executables - return false; - default: - break; - } - if ( ref.getKind() == arm::kReadOnlyPointer ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // illegal in dylibs/bundles, until we support TEXT relocs - return fSlideable; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // illegal until we support TEXT relocs - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - // absolute symbbols only allowed in static executables - return true; - } - } - return false; -} - -template <> -bool Writer<x86>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - macho_relocation_info<P> reloc; - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(sectInfo->getIndex()); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(false); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(reloc); - atomSection->fHasTextLocalRelocs = true; - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_TEXT_ABSOLUTE32, atom.getAddress() + ref.getFixUpOffset())); - } - return true; - } - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - -template <> -bool Writer<ppc>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - macho_relocation_info<P> reloc1; - macho_relocation_info<P> reloc2; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - switch ( ref.getKind() ) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); - reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc1.set_r_symbolnum(sectInfo->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(ref.getKind()==ppc::kAbsLow16 ? PPC_RELOC_LO16 : PPC_RELOC_LO14); - reloc2.set_r_address(targetAddr >> 16); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fInternalRelocs.push_back(reloc1); - fInternalRelocs.push_back(reloc2); - atomSection->fHasTextLocalRelocs = true; - return true; - } - break; - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - if ( fSlideable ) { - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - uint32_t targetAddr = ref.getTarget().getAddress() + ref.getTargetOffset(); - reloc1.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc1.set_r_symbolnum(sectInfo->getIndex()); - reloc1.set_r_pcrel(false); - reloc1.set_r_length(2); - reloc1.set_r_extern(false); - reloc1.set_r_type(ref.getKind()==ppc::kAbsHigh16AddLow ? PPC_RELOC_HA16 : PPC_RELOC_HI16); - reloc2.set_r_address(targetAddr & 0xFFFF); - reloc2.set_r_symbolnum(0); - reloc2.set_r_pcrel(false); - reloc2.set_r_length(2); - reloc2.set_r_extern(false); - reloc2.set_r_type(PPC_RELOC_PAIR); - fInternalRelocs.push_back(reloc1); - fInternalRelocs.push_back(reloc2); - atomSection->fHasTextLocalRelocs = true; - return true; - } - } - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - return false; -} - -template <> -bool Writer<arm>::generatesLocalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == arm::kReadOnlyPointer ) { - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // a reference to the absolute address of something in this same linkage unit can be - // encoded as a local text reloc in a dylib or bundle - if ( fSlideable ) { - macho_relocation_info<P> reloc; - SectionInfo* sectInfo = (SectionInfo*)(ref.getTarget().getSection()); - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(sectInfo->getIndex()); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(false); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(reloc); - atomSection->fHasTextLocalRelocs = true; - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_TEXT_ABSOLUTE32, atom.getAddress() + ref.getFixUpOffset())); - } - return true; - } - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - - -template <> -bool Writer<x86_64>::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - // text relocs not supported (usually never needed because of RIP addressing) - return false; -} - -template <> -bool Writer<ppc64>::generatesLocalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - // text relocs not supported - return false; -} - -template <> -bool Writer<x86>::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( ref.getKind() == x86::kAbsolute32 ) { - macho_relocation_info<P> reloc; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // a reference to the absolute address of something in another linkage unit can be - // encoded as an external text reloc in a dylib or bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(false); - reloc.set_r_length(); - reloc.set_r_extern(true); - reloc.set_r_type(GENERIC_RELOC_VANILLA); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - } - return false; -} - -template <> -bool Writer<x86_64>::generatesExternalTextReloc(const ObjectFile::Reference& ref, const ObjectFile::Atom& atom, SectionInfo* atomSection) -{ - if ( fOptions.outputKind() == Options::kKextBundle ) { - macho_relocation_info<P> reloc; - switch ( ref.getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - switch ( ref.getKind() ) { - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - // a branch to something in another linkage unit is - // encoded as an external text reloc in a kext bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(true); - reloc.set_r_length(2); - reloc.set_r_extern(true); - reloc.set_r_type(X86_64_RELOC_BRANCH); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - // a load of the GOT entry for a symbol in another linkage unit is - // encoded as an external text reloc in a kext bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(true); - reloc.set_r_length(2); - reloc.set_r_extern(true); - reloc.set_r_type(X86_64_RELOC_GOT_LOAD); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - // a use of the GOT entry for a symbol in another linkage unit is - // encoded as an external text reloc in a kext bundle - reloc.set_r_address(this->relocAddressInFinalLinkedImage(atom.getAddress() + ref.getFixUpOffset(), &atom)); - reloc.set_r_symbolnum(this->symbolIndex(ref.getTarget())); - reloc.set_r_pcrel(true); - reloc.set_r_length(2); - reloc.set_r_extern(true); - reloc.set_r_type(X86_64_RELOC_GOT); - fExternalRelocs.push_back(reloc); - atomSection->fHasTextExternalRelocs = true; - return true; - } - break; - } - } - return false; -} - - -template <typename A> -bool Writer<A>::generatesExternalTextReloc(const ObjectFile::Reference&, const ObjectFile::Atom& atom, SectionInfo* curSection) -{ - return false; -} - - - - -template <typename A> -typename Writer<A>::RelocKind Writer<A>::relocationNeededInFinalLinkedImage(const ObjectFile::Atom& target) const -{ - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - // in main executables, the only way regular symbols are indirected is if -interposable is used - if ( fOptions.outputKind() == Options::kDynamicExecutable ) { - if ( this->shouldExport(target) && fOptions.interposable(target.getName()) ) - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - } - // for flat-namespace or interposable two-level-namespace - // all references to exported symbols get indirected - else if ( this->shouldExport(target) && - ((fOptions.nameSpace() == Options::kFlatNameSpace) - || (fOptions.nameSpace() == Options::kForceFlatNameSpace) - || fOptions.interposable(target.getName())) - && (target.getName() != NULL) - && (strncmp(target.getName(), ".objc_class_", 12) != 0) ) // <rdar://problem/5254468> - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - case ObjectFile::Atom::kWeakDefinition: - // in static executables, references to weak definitions are not indirected - if ( fOptions.outputKind() == Options::kStaticExecutable) - return kRelocNone; - // in dynamic code, all calls to global weak definitions get indirected - if ( this->shouldExport(target) ) - return kRelocExternal; - else if ( fSlideable ) - return kRelocInternal; - else - return kRelocNone; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - return kRelocExternal; - case ObjectFile::Atom::kAbsoluteSymbol: - return kRelocNone; - } - return kRelocNone; -} - -template <typename A> -uint64_t Writer<A>::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for 32-bit architectures, the r_address field in relocs - // for final linked images is the offset from the first segment - uint64_t result = address - fSegmentInfos[0]->fBaseAddress; - if ( fOptions.outputKind() == Options::kPreload ) { - // kPreload uses a virtual __HEADER segment to cover the load commands - result = address - fSegmentInfos[1]->fBaseAddress; - } - // or the offset from the first writable segment if built split-seg - if ( fOptions.splitSeg() ) - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0x7FFFFFFF ) { - throwf("image too large: address can't fit in 31-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - -template <> -uint64_t Writer<ppc64>::relocAddressInFinalLinkedImage(uint64_t address, const ObjectFile::Atom* atom) const -{ - // for ppc64, the Mac OS X 10.4 dyld assumes r_address is always the offset from the base address. - // the 10.5 dyld, iterprets the r_address as: - // 1) an offset from the base address, iff there are no writable segments with a address > 4GB from base address, otherwise - // 2) an offset from the base address of the first writable segment - // For dyld, r_address is always the offset from the base address - uint64_t result; - bool badFor10_4 = false; - if ( fWritableSegmentPastFirst4GB ) { - if ( fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5 ) - badFor10_4 = true; - result = address - fFirstWritableSegment->fBaseAddress; - if ( result > 0xFFFFFFFF ) { - throwf("image too large: address can't fit in 32-bit r_address field in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - result = address - fSegmentInfos[0]->fBaseAddress; - if ( (fOptions.macosxVersionMin() < ObjectFile::ReaderOptions::k10_5) && (result > 0x7FFFFFFF) ) - badFor10_4 = true; - } - if ( badFor10_4 ) { - throwf("image or pagezero_size too large for Mac OS X 10.4: address can't fit in 31-bit r_address field for %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - return result; -} - - -template <> bool Writer<ppc>::preboundLazyPointerType(uint8_t* type) { *type = PPC_RELOC_PB_LA_PTR; return true; } -template <> bool Writer<ppc64>::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } -template <> bool Writer<x86>::preboundLazyPointerType(uint8_t* type) { *type = GENERIC_RELOC_PB_LA_PTR; return true; } -template <> bool Writer<x86_64>::preboundLazyPointerType(uint8_t* type) { throw "prebinding not supported"; } -template <> bool Writer<arm>::preboundLazyPointerType(uint8_t* type) { *type = ARM_RELOC_PB_LA_PTR; return true; } - -template <typename A> -void Writer<A>::buildExecutableFixups() -{ - if ( fIndirectTableAtom != NULL ) - fIndirectTableAtom->fTable.reserve(50); // minimize reallocations - std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos; - const int segCount = segmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* curSegment = segmentInfos[i]; - std::vector<SectionInfo*>& sectionInfos = curSegment->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - //fprintf(stderr, "starting section %s\n", curSection->fSectionName); - std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms; - if ( ! curSection->fAllZeroFill ) { - if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers - || curSection->fAllStubs || curSection->fAllSelfModifyingStubs ) { - if ( fIndirectTableAtom != NULL ) - curSection->fIndirectSymbolOffset = fIndirectTableAtom->fTable.size(); - } - const int atomCount = sectionAtoms.size(); - for (int k=0; k < atomCount; ++k) { - ObjectFile::Atom* atom = sectionAtoms[k]; - std::vector<ObjectFile::Reference*>& refs = atom->getReferences(); - const int refCount = refs.size(); - //fprintf(stderr, "atom %s has %d references in section %s, %p\n", atom->getDisplayName(), refCount, curSection->fSectionName, atom->getSection()); - if ( curSection->fAllNonLazyPointers && (refCount == 0) ) { - // handle imageloadercache GOT slot - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / sizeof(pint_t); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - // use INDIRECT_SYMBOL_ABS so 10.5 dyld will leave value as zero - IndirectEntry entry = { indirectTableIndex, INDIRECT_SYMBOL_ABS }; - //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n", - // indirectTableIndex, INDIRECT_SYMBOL_LOCAL, curSection->fSectionName); - fIndirectTableAtom->fTable.push_back(entry); - } - for (int l=0; l < refCount; ++l) { - ObjectFile::Reference* ref = refs[l]; - if ( (fOptions.outputKind() != Options::kKextBundle) && - (curSection->fAllNonLazyPointers || curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers) ) { - // if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol - if ( atom->getSize() != sizeof(pint_t) ) { - warning("wrong size pointer atom %s from file %s", atom->getDisplayName(), atom->getFile()->getPath()); - } - ObjectFile::Atom* pointerTarget = &(ref->getTarget()); - if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { - pointerTarget = ((LazyPointerAtom<A>*)atom)->getTarget(); - } - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / sizeof(pint_t); - uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL; - if (atom == fFastStubGOTAtom) - undefinedSymbolIndex = INDIRECT_SYMBOL_ABS; - else if ( this->relocationNeededInFinalLinkedImage(*pointerTarget) == kRelocExternal ) - undefinedSymbolIndex = this->symbolIndex(*pointerTarget); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //fprintf(stderr,"fIndirectTableAtom->fTable.push_back(tableIndex=%d, symIndex=0x%X, section=%s)\n", - // indirectTableIndex, undefinedSymbolIndex, curSection->fSectionName); - fIndirectTableAtom->fTable.push_back(entry); - if ( curSection->fAllLazyPointers || curSection->fAllLazyDylibPointers ) { - uint8_t preboundLazyType; - if ( fOptions.prebind() && (fDyldClassicHelperAtom != NULL) - && curSection->fAllLazyPointers && preboundLazyPointerType(&preboundLazyType) ) { - // this is a prebound image, need special relocs for dyld to reset lazy pointers if prebinding is invalid - macho_scattered_relocation_info<P> pblaReloc; - pblaReloc.set_r_scattered(true); - pblaReloc.set_r_pcrel(false); - pblaReloc.set_r_length(); - pblaReloc.set_r_type(preboundLazyType); - pblaReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - pblaReloc.set_r_value(fDyldClassicHelperAtom->getAddress()); - fInternalRelocs.push_back(*((macho_relocation_info<P>*)&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<P> dyldHelperReloc; - uint32_t sectionNum = 1; - if ( fDyldClassicHelperAtom != NULL ) - sectionNum = ((SectionInfo*)(fDyldClassicHelperAtom->getSection()))->getIndex(); - //fprintf(stderr, "lazy pointer reloc, section index=%u, section name=%s\n", sectionNum, curSection->fSectionName); - dyldHelperReloc.set_r_address(relocAddressInFinalLinkedImage(atom->getAddress(), atom)); - dyldHelperReloc.set_r_symbolnum(sectionNum); - dyldHelperReloc.set_r_pcrel(false); - dyldHelperReloc.set_r_length(); - dyldHelperReloc.set_r_extern(false); - dyldHelperReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(dyldHelperReloc); - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); - } - } - if ( fOptions.makeCompressedDyldInfo() ) { - uint8_t type = BIND_TYPE_POINTER; - uint64_t addresss = atom->getAddress() + ref->getFixUpOffset(); - if ( pointerTarget->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - // This is a referece to a weak def in some dylib (e.g. operator new) - // need to bind into to directly bind this - // later weak binding info may override - int ordinal = compressedOrdinalForImortedAtom(pointerTarget); - fBindingInfo.push_back(BindingInfo(type, ordinal, pointerTarget->getName(), false, addresss, 0)); - } - if ( targetRequiresWeakBinding(*pointerTarget) ) { - // note: lazy pointers to weak symbols are not bound lazily - fWeakBindingInfo.push_back(BindingInfo(type, pointerTarget->getName(), false, addresss, 0)); - } - } - } - if ( curSection->fAllNonLazyPointers && fOptions.makeCompressedDyldInfo() ) { - if ( pointerTarget != NULL ) { - switch ( this->relocationNeededInFinalLinkedImage(*pointerTarget) ) { - case kRelocNone: - // no rebase or binding info needed - break; - case kRelocInternal: - // a non-lazy pointer that has been optimized to LOCAL needs rebasing info - // but not the magic fFastStubGOTAtom atom - if (atom != fFastStubGOTAtom) - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); - break; - case kRelocExternal: - { - uint8_t type = BIND_TYPE_POINTER; - uint64_t addresss = atom->getAddress(); - if ( targetRequiresWeakBinding(ref->getTarget()) ) { - fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, 0)); - // if this is a non-lazy pointer to a weak definition within this linkage unit - // the pointer needs to initially point within linkage unit and have - // rebase command to slide it. - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - // unless if this is a hybrid format, in which case the non-lazy pointer - // is zero on disk. So use a bind instead of a rebase to set initial value - if ( fOptions.makeClassicDyldInfo() ) - fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, 0)); - else - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER,atom->getAddress())); - } - // if this is a non-lazy pointer to a weak definition in a dylib, - // the pointer needs to initially bind to the dylib - else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - int ordinal = compressedOrdinalForImortedAtom(pointerTarget); - fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, pointerTarget->getName(), false, addresss, 0)); - } - } - else { - int ordinal = compressedOrdinalForImortedAtom(pointerTarget); - bool weak_import = fWeakImportMap[pointerTarget]; - fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, 0)); - } - } - } - } - } - } - else if ( (ref->getKind() == A::kPointer) || (ref->getKind() == A::kPointerWeakImport) ) { - if ( fSlideable && ((curSegment->fInitProtection & VM_PROT_WRITE) == 0) ) { - if ( fOptions.allowTextRelocs() ) { - if ( fOptions.warnAboutTextRelocs() ) - warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); - } - else { - throwf("pointer in read-only segment not allowed in slidable image, used in %s from %s", - atom->getDisplayName(), atom->getFile()->getPath()); - } - } - switch ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) ) { - case kRelocNone: - // no reloc needed - break; - case kRelocInternal: - { - macho_relocation_info<P> internalReloc; - SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection(); - uint32_t sectionNum = sectInfo->getIndex(); - // special case _mh_dylib_header and friends which are not in any real section - if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) - sectionNum = 1; - internalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); - internalReloc.set_r_symbolnum(sectionNum); - internalReloc.set_r_pcrel(false); - internalReloc.set_r_length(); - internalReloc.set_r_extern(false); - internalReloc.set_r_type(GENERIC_RELOC_VANILLA); - fInternalRelocs.push_back(internalReloc); - if ( fOptions.makeCompressedDyldInfo() ) { - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, atom->getAddress() + ref->getFixUpOffset())); - } - } - break; - case kRelocExternal: - { - macho_relocation_info<P> externalReloc; - externalReloc.set_r_address(this->relocAddressInFinalLinkedImage(atom->getAddress() + ref->getFixUpOffset(), atom)); - externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget())); - externalReloc.set_r_pcrel(false); - externalReloc.set_r_length(); - externalReloc.set_r_extern(true); - externalReloc.set_r_type(GENERIC_RELOC_VANILLA); - fExternalRelocs.push_back(externalReloc); - if ( fOptions.makeCompressedDyldInfo() ) { - int64_t addend = ref->getTargetOffset(); - uint64_t addresss = atom->getAddress() + ref->getFixUpOffset(); - if ( !fOptions.makeClassicDyldInfo() ) { - if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - // pointers to internal weak defs need a rebase - fRebaseInfo.push_back(RebaseInfo(REBASE_TYPE_POINTER, addresss)); - } - } - uint8_t type = BIND_TYPE_POINTER; - if ( targetRequiresWeakBinding(ref->getTarget()) ) { - fWeakBindingInfo.push_back(BindingInfo(type, ref->getTarget().getName(), false, addresss, addend)); - if ( fOptions.makeClassicDyldInfo() && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // hybrid linkedit puts addend in data, so we need bind phase to reset pointer to local definifion - fBindingInfo.push_back(BindingInfo(type, BIND_SPECIAL_DYLIB_SELF, ref->getTarget().getName(), false, addresss, addend)); - } - // if this is a pointer to a weak definition in a dylib, - // the pointer needs to initially bind to the dylib - else if ( ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition ) { - int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); - fBindingInfo.push_back(BindingInfo(BIND_TYPE_POINTER, ordinal, ref->getTarget().getName(), false, addresss, addend)); - } - } - else { - int ordinal = compressedOrdinalForImortedAtom(&ref->getTarget()); - bool weak_import = fWeakImportMap[&(ref->getTarget())]; - fBindingInfo.push_back(BindingInfo(type, ordinal, ref->getTarget().getName(), weak_import, addresss, addend)); - } - } - } - break; - } - } - else if ( this->illegalRelocInFinalLinkedImage(*ref) ) { - // new x86 stubs always require text relocs - if ( curSection->fAllStubs || curSection->fAllStubHelpers ) { - if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fInternalRelocs - } - } - else if ( fOptions.allowTextRelocs() && !atom->getSegment().isContentWritable() ) { - if ( fOptions.warnAboutTextRelocs() ) - warning("text reloc in %s to %s", atom->getDisplayName(), ref->getTargetName()); - if ( this->generatesLocalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fInternalRelocs - } - else if ( this->generatesExternalTextReloc(*ref, *atom, curSection) ) { - // relocs added to fExternalRelocs - } - else { - throwf("relocation used in %s from %s not allowed in slidable image", atom->getDisplayName(), atom->getFile()->getPath()); - } - } - else { - throwf("absolute addressing (perhaps -mdynamic-no-pic) used in %s from %s not allowed in slidable image. " - "Use '-read_only_relocs suppress' to enable text relocs", atom->getDisplayName(), atom->getFile()->getPath()); - } - } - } - if ( curSection->fAllSelfModifyingStubs || curSection->fAllStubs ) { - ObjectFile::Atom* stubTarget = ((StubAtom<A>*)atom)->getTarget(); - uint32_t undefinedSymbolIndex = (stubTarget != NULL) ? this->symbolIndex(*stubTarget) : INDIRECT_SYMBOL_ABS; - uint32_t offsetInSection = atom->getSectionOffset(); - uint32_t indexInSection = offsetInSection / atom->getSize(); - uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset; - IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex }; - //fprintf(stderr,"for stub: fIndirectTableAtom->fTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, stubTarget->getName(), atom->getSize()); - fIndirectTableAtom->fTable.push_back(entry); - } - } - } - } - } - if ( fSplitCodeToDataContentAtom != NULL ) - fSplitCodeToDataContentAtom->encode(); - if ( fCompressedRebaseInfoAtom != NULL ) - fCompressedRebaseInfoAtom->encode(); - if ( fCompressedBindingInfoAtom != NULL ) - fCompressedBindingInfoAtom->encode(); - if ( fCompressedWeakBindingInfoAtom != NULL ) - fCompressedWeakBindingInfoAtom->encode(); - if ( fCompressedLazyBindingInfoAtom != NULL ) - fCompressedLazyBindingInfoAtom->encode(); - if ( fCompressedExportInfoAtom != NULL ) - fCompressedExportInfoAtom->encode(); -} - - -template <> -void Writer<ppc>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (ppc::ReferenceKinds)ref->getKind() ) { - case ppc::kPICBaseHigh16: - fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); - break; - case ppc::kPointerDiff32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc::kPointerDiff64: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc::kNoFixUp: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - // ignore - break; - default: - warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer<ppc64>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (ppc64::ReferenceKinds)ref->getKind() ) { - case ppc64::kPICBaseHigh16: - fSplitCodeToDataContentAtom->addPPCHi16Location(atom, ref->getFixUpOffset()); - break; - case ppc64::kPointerDiff32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc64::kPointerDiff64: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case ppc64::kNoFixUp: - case ppc64::kGroupSubordinate: - case ppc64::kPointer: - case ppc64::kPointerWeakImport: - case ppc64::kPICBaseLow16: - case ppc64::kPICBaseLow14: - // ignore - break; - default: - warning("codegen with reference kind %d in %s prevents image from loading in dyld shared cache", ref->getKind(), atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer<x86>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (x86::ReferenceKinds)ref->getKind() ) { - case x86::kPointerDiff: - case x86::kImageOffset32: - if ( strcmp(ref->getTarget().getSegment().getName(), "__IMPORT") == 0 ) - fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); - else - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86::kNoFixUp: - case x86::kGroupSubordinate: - case x86::kPointer: - case x86::kPointerWeakImport: - // ignore - break; - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - if ( (&(ref->getTarget().getSegment()) == &Segment::fgImportSegment) - || (&(ref->getTarget().getSegment()) == &Segment::fgROImportSegment) ) { - fSplitCodeToDataContentAtom->add32bitImportLocation(atom, ref->getFixUpOffset()); - break; - } - // fall into warning case - default: - if ( fOptions.makeCompressedDyldInfo() && (ref->getKind() == x86::kAbsolute32) ) { - // will be encoded in rebase info - } - else { - warning("codegen in %s (offset 0x%08llX) prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getFixUpOffset()); - fSplitCodeToDataContentAtom->setCantEncode(); - } - } -} - -template <> -void Writer<x86_64>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (x86_64::ReferenceKinds)ref->getKind() ) { - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPointerDiff32: - case x86_64::kImageOffset32: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86_64::kPointerDiff: - fSplitCodeToDataContentAtom->add64bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case x86_64::kNoFixUp: - case x86_64::kGroupSubordinate: - case x86_64::kPointer: - case x86_64::kGOTNoFixUp: - // ignore - break; - default: - warning("codegen in %s with kind %d prevents image from loading in dyld shared cache", atom->getDisplayName(), ref->getKind()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <> -void Writer<arm>::addCrossSegmentRef(const ObjectFile::Atom* atom, const ObjectFile::Reference* ref) -{ - switch ( (arm::ReferenceKinds)ref->getKind() ) { - case arm::kPointerDiff: - fSplitCodeToDataContentAtom->add32bitPointerLocation(atom, ref->getFixUpOffset()); - break; - case arm::kNoFixUp: - case arm::kGroupSubordinate: - case arm::kPointer: - case arm::kPointerWeakImport: - case arm::kReadOnlyPointer: - // ignore - break; - default: - warning("codegen in %s prevents image from loading in dyld shared cache", atom->getDisplayName()); - fSplitCodeToDataContentAtom->setCantEncode(); - } -} - -template <typename A> -bool Writer<A>::segmentsCanSplitApart(const ObjectFile::Atom& from, const ObjectFile::Atom& to) -{ - switch ( to.getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // segments with same permissions slide together - return ( (from.getSegment().isContentExecutable() != to.getSegment().isContentExecutable()) - || (from.getSegment().isContentWritable() != to.getSegment().isContentWritable()) ); - } - throw "ld64 internal error"; -} - - -template <> -void Writer<ppc>::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint32_t ppcNop; - OSWriteBigInt32(&ppcNop, 0, 0x60000000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &ppcNop, 4, p); -} - -template <> -void Writer<ppc64>::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint32_t ppcNop; - OSWriteBigInt32(&ppcNop, 0, 0x60000000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &ppcNop, 4, p); -} - -template <> -void Writer<x86>::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint8_t x86Nop = 0x90; - for (uint32_t p=from; p < to; ++p) - ::pwrite(fd, &x86Nop, 1, p); -} - -template <> -void Writer<x86_64>::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - uint8_t x86Nop = 0x90; - for (uint32_t p=from; p < to; ++p) - ::pwrite(fd, &x86Nop, 1, p); -} - -template <> -void Writer<arm>::writeNoOps(int fd, uint32_t from, uint32_t to) -{ - // FIXME: need thumb nop? - uint32_t armNop; - OSWriteLittleInt32(&armNop, 0, 0xe1a00000); - for (uint32_t p=from; p < to; p += 4) - ::pwrite(fd, &armNop, 4, p); -} - -template <> -void Writer<ppc>::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<ppc64>::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<x86>::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; ++p) - *p = 0x90; -} - -template <> -void Writer<x86_64>::copyNoOps(uint8_t* from, uint8_t* to) -{ - for (uint8_t* p=from; p < to; ++p) - *p = 0x90; -} - -template <> -void Writer<arm>::copyNoOps(uint8_t* from, uint8_t* to) -{ - // fixme: need thumb nop? - for (uint8_t* p=from; p < to; p += 4) - OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); -} - -static const char* stringName(const char* str) -{ - if ( strncmp(str, "cstring=", 8) == 0) { - static char buffer[1024]; - char* t = buffer; - *t++ = '\"'; - for(const char*s = &str[8]; *s != '\0'; ++s) { - switch(*s) { - case '\n': - *t++ = '\\'; - *t++ = 'n'; - break; - case '\t': - *t++ = '\\'; - *t++ = 't'; - break; - default: - *t++ = *s; - break; - } - if ( t > &buffer[1020] ) { - *t++= '\"'; - *t++= '.'; - *t++= '.'; - *t++= '.'; - *t++= '\0'; - return buffer; - } - } - *t++= '\"'; - *t++= '\0'; - return buffer; - } - else { - return str; - } -} - - -template <> const char* Writer<ppc>::getArchString() { return "ppc"; } -template <> const char* Writer<ppc64>::getArchString() { return "ppc64"; } -template <> const char* Writer<x86>::getArchString() { return "i386"; } -template <> const char* Writer<x86_64>::getArchString() { return "x86_64"; } -template <> const char* Writer<arm>::getArchString() { return "arm"; } - -template <typename A> -void Writer<A>::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<ObjectFile::Reader*, uint32_t> readerToOrdinal; - std::map<uint32_t, ObjectFile::Reader*> ordinalToReader; - std::map<ObjectFile::Reader*, uint32_t> readerToFileOrdinal; - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections; - for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - std::vector<ObjectFile::Atom*>& sectionAtoms = (*secit)->fAtoms; - for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Reader* reader = (*ait)->getFile(); - uint32_t readerOrdinal = (*ait)->getOrdinal(); - std::map<ObjectFile::Reader*, uint32_t>::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<uint32_t, ObjectFile::Reader*>::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<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections; - for (std::vector<SectionInfo*>::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<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections; - for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - if ( ! (*secit)->fVirtualSection ) { - std::vector<ObjectFile::Atom*>& sectionAtoms = (*secit)->fAtoms; - bool isCstring = (strcmp((*secit)->fSectionName, "__cstring") == 0); - for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->getAddress(), atom->getSize(), - readerToFileOrdinal[atom->getFile()], isCstring ? stringName(atom->getDisplayName()): atom->getDisplayName()); - } - } - } - } - fclose(mapFile); - } - else { - warning("could not write map file: %s\n", fOptions.generatedMapPath()); - } - } -} - -static const char* sCleanupFile = NULL; -static void cleanup(int sig) -{ - ::signal(sig, SIG_DFL); - if ( sCleanupFile != NULL ) { - ::unlink(sCleanupFile); - } - if ( sig == SIGINT ) - ::exit(1); -} - - -template <typename A> -uint64_t Writer<A>::writeAtoms() -{ - // for UNIX conformance, error if file exists and is not writable - if ( (access(fFilePath, F_OK) == 0) && (access(fFilePath, W_OK) == -1) ) - throwf("can't write output file: %s", fFilePath); - - int permissions = 0777; - if ( fOptions.outputKind() == Options::kObjectFile ) - permissions = 0666; - // Calling unlink first assures the file is gone so that open creates it with correct permissions - // It also handles the case where fFilePath file is not writable but its directory is - // And it means we don't have to truncate the file when done writing (in case new is smaller than old) - (void)unlink(fFilePath); - - // try to allocate buffer for entire output file content - int fd = -1; - SectionInfo* lastSection = fSegmentInfos.back()->fSections.back(); - uint64_t fileBufferSize = (lastSection->fFileOffset + lastSection->fSize + 4095) & (-4096); - uint8_t* wholeBuffer = (uint8_t*)calloc(fileBufferSize, 1); - uint8_t* atomBuffer = NULL; - bool streaming = false; - if ( wholeBuffer == NULL ) { - fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); - atomBuffer = new uint8_t[(fLargestAtomSize+4095) & (-4096)]; - streaming = true; - // install signal handlers to delete output file if program is killed - sCleanupFile = fFilePath; - ::signal(SIGINT, cleanup); - ::signal(SIGBUS, cleanup); - ::signal(SIGSEGV, cleanup); - } - uint32_t size = 0; - uint32_t end = 0; - try { - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - std::vector<SectionInfo*>& sectionInfos = curSegment->fSections; - for (std::vector<SectionInfo*>::iterator secit = sectionInfos.begin(); secit != sectionInfos.end(); ++secit) { - SectionInfo* curSection = *secit; - std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms; - //printf("writing with max atom size 0x%X\n", fLargestAtomSize); - //fprintf(stderr, "writing %lu atoms for section %p %s at file offset 0x%08llX\n", sectionAtoms.size(), curSection, curSection->fSectionName, curSection->fFileOffset); - if ( ! curSection->fAllZeroFill ) { - bool needsNops = ((strcmp(curSection->fSegmentName, "__TEXT") == 0) && (strncmp(curSection->fSectionName, "__text", 6) == 0)); - for (std::vector<ObjectFile::Atom*>::iterator ait = sectionAtoms.begin(); ait != sectionAtoms.end(); ++ait) { - ObjectFile::Atom* atom = *ait; - if ( (atom->getDefinitionKind() != ObjectFile::Atom::kExternalDefinition) - && (atom->getDefinitionKind() != ObjectFile::Atom::kExternalWeakDefinition) - && (atom->getDefinitionKind() != ObjectFile::Atom::kAbsoluteSymbol) ) { - uint32_t fileOffset = curSection->fFileOffset + atom->getSectionOffset(); - if ( fileOffset != end ) { - //fprintf(stderr, "writing %d pad bytes, needsNops=%d\n", fileOffset-end, needsNops); - if ( needsNops ) { - // fill gaps with no-ops - if ( streaming ) - writeNoOps(fd, end, fileOffset); - else - copyNoOps(&wholeBuffer[end], &wholeBuffer[fileOffset]); - } - else if ( streaming ) { - // zero fill gaps - if ( (fileOffset-end) == 4 ) { - uint32_t zero = 0; - ::pwrite(fd, &zero, 4, end); - } - else { - uint8_t zero = 0x00; - for (uint32_t p=end; p < fileOffset; ++p) - ::pwrite(fd, &zero, 1, p); - } - } - } - uint64_t atomSize = atom->getSize(); - if ( streaming ) { - if ( atomSize > fLargestAtomSize ) - throwf("ld64 internal error: atom \"%s\"is larger than expected 0x%llX > 0x%X", - atom->getDisplayName(), atomSize, fLargestAtomSize); - } - else { - if ( fileOffset > fileBufferSize ) - throwf("ld64 internal error: atom \"%s\" has file offset greater thatn expceted 0x%X > 0x%llX", - atom->getDisplayName(), fileOffset, fileBufferSize); - } - uint8_t* buffer = streaming ? atomBuffer : &wholeBuffer[fileOffset]; - end = fileOffset+atomSize; - // copy raw bytes - atom->copyRawContent(buffer); - // apply any fix-ups - try { - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* ref = *it; - if ( fOptions.outputKind() == Options::kObjectFile ) { - // doing ld -r - // skip fix-ups for undefined targets - if ( &(ref->getTarget()) != NULL ) - this->fixUpReferenceRelocatable(ref, atom, buffer); - } - else { - // producing final linked image - this->fixUpReferenceFinal(ref, atom, buffer); - } - } - } - catch (const char* msg) { - throwf("%s in %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath()); - } - //fprintf(stderr, "writing 0x%08X -> 0x%08X (addr=0x%llX, size=0x%llX), atom %p %s from %s\n", - // fileOffset, end, atom->getAddress(), atom->getSize(), atom, atom->getDisplayName(), atom->getFile()->getPath()); - if ( streaming ) { - // write out - ::pwrite(fd, buffer, atomSize, fileOffset); - } - else { - if ( (fileOffset + atomSize) > size ) - size = fileOffset + atomSize; - } - } - } - } - } - } - - // update content based UUID - if ( fOptions.getUUIDMode() == Options::kUUIDContent ) { - uint8_t digest[CC_MD5_DIGEST_LENGTH]; - if ( streaming ) { - // if output file file did not fit in memory, re-read file to generate md5 hash - uint32_t kMD5BufferSize = 16*1024; - uint8_t* md5Buffer = (uint8_t*)::malloc(kMD5BufferSize); - if ( md5Buffer != NULL ) { - CC_MD5_CTX md5State; - CC_MD5_Init(&md5State); - ::lseek(fd, 0, SEEK_SET); - ssize_t len; - while ( (len = ::read(fd, md5Buffer, kMD5BufferSize)) > 0 ) - CC_MD5_Update(&md5State, md5Buffer, len); - CC_MD5_Final(digest, &md5State); - ::free(md5Buffer); - } - else { - // if malloc fails, fall back to random uuid - ::uuid_generate_random(digest); - } - fUUIDAtom->setContent(digest); - uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); - fUUIDAtom->copyRawContent(atomBuffer); - ::pwrite(fd, atomBuffer, fUUIDAtom->getSize(), uuidOffset); - } - else { - // if output file fit in memory, just genrate an md5 hash in memory - #if 1 - // temp hack for building on Tiger - CC_MD5_CTX md5State; - CC_MD5_Init(&md5State); - CC_MD5_Update(&md5State, wholeBuffer, size); - CC_MD5_Final(digest, &md5State); - #else - CC_MD5(wholeBuffer, size, digest); - #endif - fUUIDAtom->setContent(digest); - uint32_t uuidOffset = ((SectionInfo*)fUUIDAtom->getSection())->fFileOffset + fUUIDAtom->getSectionOffset(); - fUUIDAtom->copyRawContent(&wholeBuffer[uuidOffset]); - } - } - } - catch (...) { - if ( sCleanupFile != NULL ) - ::unlink(sCleanupFile); - throw; - } - - // finish up - if ( streaming ) { - delete [] atomBuffer; - close(fd); - // restore default signal handlers - sCleanupFile = NULL; - ::signal(SIGINT, SIG_DFL); - ::signal(SIGBUS, SIG_DFL); - ::signal(SIGSEGV, SIG_DFL); - } - else { - // write whole output file in one chunk - fd = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions); - if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", fFilePath, errno); - ::pwrite(fd, wholeBuffer, size, 0); - close(fd); - delete [] wholeBuffer; - } - - return end; -} - -template <> -void Writer<arm>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - int64_t displacement; - int64_t baseAddr; - uint32_t instruction; - uint32_t newInstruction; - uint64_t targetAddr = 0; - uint32_t firstDisp; - uint32_t nextDisp; - uint32_t opcode = 0; - int32_t diff; - bool relocateableExternal = false; - bool is_bl; - bool is_blx; - bool targetIsThumb; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); - } - - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - switch ( (arm::ReferenceKinds)(ref->getKind()) ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - // do nothing - break; - case arm::kPointerWeakImport: - case arm::kPointer: - // If this is the lazy pointers section, then set all lazy pointers to - // point to the dyld stub binding helper. - if ( ((SectionInfo*)inAtom->getSection())->fAllLazyPointers - || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound lazy pointer to another dylib ==> pointer contains zero - LittleEndian::set32(*fixUp, 0); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - // prebound lazy pointer to withing this dylib ==> pointer contains address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - break; - } - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else if ( !fOptions.makeClassicDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition - if ( ref->getTarget().isThumb() ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - else { - // pointer contains target address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - break; - case arm::kPointerDiff: - diff = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - diff |= 1; - LittleEndian::set32(*fixUp, diff); - break; - case arm::kReadOnlyPointer: - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0)) - targetAddr |= 1; - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // pointer contains target address - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - // pointer contains target address - LittleEndian::set32(*fixUp, targetAddr); - break; - } - break; - case arm::kBranch24WeakImport: - case arm::kBranch24: - displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - // check if this is a branch to a branch island that can be skipped - if ( ref->getTarget().getContentType() == ObjectFile::Atom::kBranchIsland ) { - uint64_t finalTargetAddress = ((BranchIslandAtom<arm>*)(&(ref->getTarget())))->getFinalTargetAdress(); - int64_t altDisplacment = finalTargetAddress - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( (altDisplacment < 33554428LL) && (altDisplacment > (-33554432LL)) ) { - //fprintf(stderr, "using altDisplacment = %lld\n", altDisplacment); - // yes, we can skip the branch island - displacement = altDisplacment; - } - } - // The pc added will be +8 from the pc - displacement -= 8; - //fprintf(stderr, "bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); - // max positive displacement is 0x007FFFFF << 2 - // max negative displacement is 0xFF800000 << 2 - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - throwf("b/bl/blx out of range (%lld max is +/-32M) from 0x%08llX %s in %s to 0x%08llX %s in %s", - displacement, inAtom->getAddress(), inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getAddress(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - instruction = LittleEndian::get32(*fixUp); - // Make sure we are calling arm with bl, thumb with blx - is_bl = ((instruction & 0xFF000000) == 0xEB000000); - is_blx = ((instruction & 0xFE000000) == 0xFA000000); - if ( is_bl && ref->getTarget().isThumb() ) { - uint32_t opcode = 0xFA000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; - newInstruction = opcode | h_bit | disp; - } - else if ( is_blx && !ref->getTarget().isThumb() ) { - uint32_t opcode = 0xEB000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - newInstruction = opcode | disp; - } - else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { - throwf("don't know how to convert instruction %x referencing %s to thumb", - instruction, ref->getTarget().getDisplayName()); - } - else { - newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); - } - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - instruction = LittleEndian::get32(*fixUp); - is_bl = ((instruction & 0xD000F800) == 0xD000F000); - is_blx = ((instruction & 0xD000F800) == 0xC000F000); - targetIsThumb = ref->getTarget().isThumb(); - - // The pc added will be +4 from the pc - baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; - // If the target is not thumb, we will be generating a blx instruction - // Since blx cannot have the low bit set, set bit[1] of the target to - // bit[1] of the base address, so that the difference is a multiple of - // 4 bytes. - if ( !targetIsThumb ) { - targetAddr &= -3ULL; - targetAddr |= (baseAddr & 2LL); - } - displacement = targetAddr - baseAddr; - - // max positive displacement is 0x003FFFFE - // max negative displacement is 0xFFC00000 - if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - // armv7 supports a larger displacement - if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { - if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { - throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - else { - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - if ( is_bl ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // keep bl - else - opcode = 0xC000F000; // change to blx - } - else if ( is_blx ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // change to bl - else - opcode = 0xC000F000; // keep blx - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - nextDisp = (j1 << 13) | (j2 << 11) | imm11; - firstDisp = (s << 10) | imm10; - newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - LittleEndian::set32(*fixUp, newInstruction); - } - } - else { - throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - else { - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; - nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; - if ( is_bl && !targetIsThumb ) { - opcode = 0xE800F000; - } - else if ( is_blx && targetIsThumb ) { - opcode = 0xF800F000; - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - else { - opcode = instruction & 0xF800F800; - } - newInstruction = opcode | (nextDisp << 16) | firstDisp; - LittleEndian::set32(*fixUp, newInstruction); - } - break; - case arm::kDtraceProbeSite: - if ( inAtom->isThumb() ) { - // change 32-bit blx call site to two thumb NOPs - LittleEndian::set32(*fixUp, 0x46C046C0); - } - else { - // change call site to a NOP - LittleEndian::set32(*fixUp, 0xE1A00000); - } - break; - case arm::kDtraceIsEnabledSite: - if ( inAtom->isThumb() ) { - // change 32-bit blx call site to 'nop', 'eor r0, r0' - LittleEndian::set32(*fixUp, 0x46C04040); - } - else { - // change call site to 'eor r0, r0, r0' - LittleEndian::set32(*fixUp, 0xE0200000); - } - break; - case arm::kDtraceTypeReference: - case arm::kDtraceProbe: - // nothing to fix up - break; - case arm::kPointerDiff12: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > 4092LL) || (displacement <-4092LL) ) { - throwf("ldr 12-bit displacement out of range (%lld max +/-4096) in %s", displacement, inAtom->getDisplayName()); - } - instruction = LittleEndian::get32(*fixUp); - if ( displacement >= 0 ) { - instruction &= 0xFFFFF000; - instruction |= ((uint32_t)displacement & 0xFFF); - } - else { - instruction &= 0xFF7FF000; - instruction |= ((uint32_t)(-displacement) & 0xFFF); - } - LittleEndian::set32(*fixUp, instruction); - break; - } -} - -template <> -void Writer<arm>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - int64_t displacement; - uint32_t instruction; - uint32_t newInstruction; - uint64_t targetAddr = 0; - int64_t baseAddr; - uint32_t firstDisp; - uint32_t nextDisp; - uint32_t opcode = 0; - int32_t diff; - bool relocateableExternal = false; - bool is_bl; - bool is_blx; - bool targetIsThumb; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); - } - - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - switch ( (arm::ReferenceKinds)(ref->getKind()) ) { - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - // do nothing - break; - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // indirect symbol table has INDIRECT_SYMBOL_LOCAL, so we must put address in content - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - LittleEndian::set32(*fixUp, targetAddr); - else - LittleEndian::set32(*fixUp, 0); - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - LittleEndian::set32(*fixUp, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - } - else { - // internal relocation => pointer contains target address - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - targetAddr |= 1; - LittleEndian::set32(*fixUp, targetAddr); - } - break; - case arm::kPointerDiff: - diff = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( ref->getTarget().isThumb() && (ref->getTargetOffset() == 0) ) - diff |= 1; - LittleEndian::set32(*fixUp, diff); - break; - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - case arm::kBranch24WeakImport: - case arm::kBranch24: - displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - // The pc added will be +8 from the pc - displacement -= 8; - // fprintf(stderr, "b/bl/blx fixup to %s at 0x%08llX, displacement = 0x%08llX\n", ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), displacement); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - else { - // max positive displacement is 0x007FFFFF << 2 - // max negative displacement is 0xFF800000 << 2 - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - throwf("arm b/bl/blx out of range (%lld max is +/-32M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - instruction = LittleEndian::get32(*fixUp); - // Make sure we are calling arm with bl, thumb with blx - is_bl = ((instruction & 0xFF000000) == 0xEB000000); - is_blx = ((instruction & 0xFE000000) == 0xFA000000); - if ( is_bl && ref->getTarget().isThumb() ) { - uint32_t opcode = 0xFA000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - uint32_t h_bit = (uint32_t)(displacement << 23) & 0x01000000; - newInstruction = opcode | h_bit | disp; - } - else if ( is_blx && !ref->getTarget().isThumb() ) { - uint32_t opcode = 0xEB000000; - uint32_t disp = (uint32_t)(displacement >> 2) & 0x00FFFFFF; - newInstruction = opcode | disp; - } - else if ( !is_bl && !is_blx && ref->getTarget().isThumb() ) { - throwf("don't know how to convert instruction %x referencing %s to thumb", - instruction, ref->getTarget().getDisplayName()); - } - else { - newInstruction = (instruction & 0xFF000000) | ((uint32_t)(displacement >> 2) & 0x00FFFFFF); - } - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kThumbBranch22WeakImport: - case arm::kThumbBranch22: - instruction = LittleEndian::get32(*fixUp); - is_bl = ((instruction & 0xF8000000) == 0xF8000000); - is_blx = ((instruction & 0xF8000000) == 0xE8000000); - targetIsThumb = ref->getTarget().isThumb(); - - // The pc added will be +4 from the pc - baseAddr = inAtom->getAddress() + ref->getFixUpOffset() + 4; - // If the target is not thumb, we will be generating a blx instruction - // Since blx cannot have the low bit set, set bit[1] of the target to - // bit[1] of the base address, so that the difference is a multiple of - // 4 bytes. - if (!targetIsThumb) { - targetAddr &= -3ULL; - targetAddr |= (baseAddr & 2LL); - } - displacement = targetAddr - baseAddr; - - //fprintf(stderr, "thumb %s fixup to %s at 0x%08llX, baseAddr = 0x%08llX, displacement = 0x%08llX, %d\n", is_blx ? "blx" : "bl", ref->getTarget().getDisplayName(), targetAddr, baseAddr, displacement, targetIsThumb); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - - if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - // armv7 supports a larger displacement - if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { - if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { - throwf("thumb bl/blx out of range (%lld max is +/-16M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - else { - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - if ( is_bl ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // keep bl - else - opcode = 0xC000F000; // change to blx - } - else if ( is_blx ) { - if ( targetIsThumb ) - opcode = 0xD000F000; // change to bl - else - opcode = 0xC000F000; // keep blx - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - nextDisp = (j1 << 13) | (j2 << 11) | imm11; - firstDisp = (s << 10) | imm10; - newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - LittleEndian::set32(*fixUp, newInstruction); - break; - } - } - else { - throwf("thumb bl/blx out of range (%lld max is +/-4M) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the first - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the next - // 11 bits of the displacement, as well as differentiating bl and blx. - firstDisp = (uint32_t)(displacement >> 12) & 0x7FF; - nextDisp = (uint32_t)(displacement >> 1) & 0x7FF; - if ( is_bl && !targetIsThumb ) { - opcode = 0xE800F000; - } - else if ( is_blx && targetIsThumb ) { - opcode = 0xF800F000; - } - else if ( !is_bl && !is_blx && !targetIsThumb ) { - throwf("don't know how to convert instruction %x referencing %s to arm", - instruction, ref->getTarget().getDisplayName()); - } - else { - opcode = instruction & 0xF800F800; - } - newInstruction = opcode | (nextDisp << 16) | firstDisp; - LittleEndian::set32(*fixUp, newInstruction); - break; - case arm::kDtraceProbe: - case arm::kDtraceTypeReference: - // nothing to fix up - break; - case arm::kPointerDiff12: - throw "internal error. no reloc for 12-bit pointer diffs"; - } -} - -template <> -void Writer<x86>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - uint8_t* dtraceProbeSite; - const int64_t kTwoGigLimit = 0x7FFFFFFF; - const int64_t kSixteenMegLimit = 0x00FFFFFF; - const int64_t kSixtyFourKiloLimit = 0x7FFF; - const int64_t kOneTwentyEightLimit = 0x7F; - int64_t displacement; - uint32_t temp; - x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - // do nothing - break; - case x86::kPointerWeakImport: - case x86::kPointer: - { - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else if ( !fOptions.makeClassicDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - else { - // pointer contains target address - //printf("Atom::fixUpReferenceFinal() target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86::kPointerDiff: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - LittleEndian::set32(*fixUp, (uint32_t)displacement); - break; - case x86::kPointerDiff16: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) - throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - break; - case x86::kPointerDiff24: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*fixUp); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*fixUp, temp); - break; - case x86::kSectionOffset24: - displacement = ref->getTarget().getSectionOffset(); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*fixUp); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*fixUp, temp); - break; - case x86::kDtraceProbeSite: - // change call site to a NOP - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x90; // 1-byte nop - dtraceProbeSite[0] = 0x0F; // 4-byte nop - dtraceProbeSite[1] = 0x1F; - dtraceProbeSite[2] = 0x40; - dtraceProbeSite[3] = 0x00; - break; - case x86::kDtraceIsEnabledSite: - // change call site to a clear eax - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x33; // xorl eax,eax - dtraceProbeSite[0] = 0xC0; - dtraceProbeSite[1] = 0x90; // 1-byte nop - dtraceProbeSite[2] = 0x90; // 1-byte nop - dtraceProbeSite[3] = 0x90; // 1-byte nop - break; - case x86::kPCRel32WeakImport: - case x86::kPCRel32: - case x86::kPCRel16: - case x86::kPCRel8: - displacement = 0; - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throw "codegen problem, can't use rel32 to external symbol"; - case ObjectFile::Atom::kTentativeDefinition: - displacement = 0; - break; - case ObjectFile::Atom::kAbsoluteSymbol: - displacement = (ref->getTarget().getSectionOffset() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - } - if ( kind == x86::kPCRel8 ) { - displacement += 3; - if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel8 out of range in %s", inAtom->getDisplayName()); - } - *(int8_t*)fixUp = (int8_t)displacement; - } - else if ( kind == x86::kPCRel16 ) { - displacement += 2; - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel16 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - } - else { - if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel32 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set32(*fixUp, (int32_t)displacement); - } - break; - case x86::kAbsolute32: - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - // pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getSectionOffset() + ref->getTargetOffset()); - break; - } - break; - case x86::kImageOffset32: - // offset of target atom from mach_header - displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress(); - LittleEndian::set32(*fixUp, (int32_t)displacement); - break; - case x86::kDtraceTypeReference: - case x86::kDtraceProbe: - // nothing to fix up - break; - } -} - - - -template <> -void Writer<x86>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t kTwoGigLimit = 0x7FFFFFFF; - const int64_t kSixtyFourKiloLimit = 0x7FFF; - const int64_t kOneTwentyEightLimit = 0x7F; - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - bool isExtern = this->makesExternalRelocatableReference(ref->getTarget()); - int64_t displacement; - x86::ReferenceKinds kind = (x86::ReferenceKinds)(ref->getKind()); - switch ( kind ) { - case x86::kNoFixUp: - case x86::kFollowOn: - case x86::kGroupSubordinate: - // do nothing - break; - case x86::kPointer: - case x86::kPointerWeakImport: - case x86::kAbsolute32: - { - if ( ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - else - LittleEndian::set32(*fixUp, 0); - } - else if ( isExtern ) { - // external relocation ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - else if ( ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) { - // internal relocation => pointer contains target address - LittleEndian::set32(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - else { - // internal relocation to tentative ==> pointer contains addend - LittleEndian::set32(*fixUp, ref->getTargetOffset()); - } - } - break; - case x86::kPointerDiff: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - LittleEndian::set32(*fixUp, (uint32_t)displacement); - break; - case x86::kPointerDiff16: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) - throwf("16-bit pointer diff out of range in %s", inAtom->getDisplayName()); - LittleEndian::set16(*((uint16_t*)fixUp), (uint16_t)displacement); - break; - case x86::kPCRel8: - case x86::kPCRel16: - case x86::kPCRel32: - case x86::kPCRel32WeakImport: - case x86::kDtraceProbeSite: - case x86::kDtraceIsEnabledSite: - { - if ( isExtern ) - displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - else - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - if ( kind == x86::kPCRel8 ) { - displacement += 3; - if ( (displacement > kOneTwentyEightLimit) || (displacement < -(kOneTwentyEightLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel8 out of range (%lld)in %s", displacement, inAtom->getDisplayName()); - } - int8_t byte = (int8_t)displacement; - *((int8_t*)fixUp) = byte; - } - else if ( kind == x86::kPCRel16 ) { - displacement += 2; - if ( (displacement > kSixtyFourKiloLimit) || (displacement < -(kSixtyFourKiloLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("rel16 out of range in %s", inAtom->getDisplayName()); - } - int16_t word = (int16_t)displacement; - LittleEndian::set16(*((uint16_t*)fixUp), word); - } - else { - if ( (displacement > kTwoGigLimit) || (displacement < (-kTwoGigLimit)) ) { - //fprintf(stderr, "call out of range, displacement=ox%llX, from %s in %s to %s in %s\n", displacement, - // inAtom->getDisplayName(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - throwf("rel32 out of range in %s", inAtom->getDisplayName()); - } - LittleEndian::set32(*fixUp, (int32_t)displacement); - } - } - break; - case x86::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - case x86::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - case x86::kDtraceProbe: - case x86::kDtraceTypeReference: - // nothing to fix up - break; - } -} - -template <> -void Writer<x86_64>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t twoGigLimit = 0x7FFFFFFF; - const int64_t kSixteenMegLimit = 0x00FFFFFF; - uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; - uint8_t* dtraceProbeSite; - int64_t displacement = 0; - uint32_t temp; - switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { - case x86_64::kNoFixUp: - case x86_64::kGOTNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - // do nothing - break; - case x86_64::kPointerWeakImport: - case x86_64::kPointer: - { - if ( &ref->getTarget() != NULL ) { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal) { - if ( !fOptions.makeClassicDyldInfo() - && (ref->getTarget().getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) ) { - // when using only compressed dyld info, pointer is initially set to point directly to weak definition - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - else { - // external relocation ==> pointer contains addend - LittleEndian::set64(*fixUp, ref->getTargetOffset()); - } - } - else { - // internal relocation - // pointer contains target address - //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - } - break; - case x86_64::kPointer32: - { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ) { - // external relocation - throwf("32-bit pointer to dylib or weak symbol %s not supported for x86_64",ref->getTarget().getDisplayName()); - } - else { - // internal relocation - // pointer contains target address - //printf("Atom::fixUpReferenceFinal) target.name=%s, target.address=0x%08llX\n", target.getDisplayName(), target.getAddress()); - displacement = ref->getTarget().getAddress() + ref->getTargetOffset(); - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - case Options::kPreload: - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kKextBundle: - throwf("32-bit pointer to symbol %s not supported for x86_64",ref->getTarget().getDisplayName()); - case Options::kDynamicExecutable: - // <rdar://problem/5855588> allow x86_64 main executables to use 32-bit pointers if program loads in load 2GB - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) - throw "32-bit pointer out of range"; - break; - case Options::kStaticExecutable: - // <rdar://problem/5855588> allow x86_64 mach_kernel to truncate pointers - break; - } - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); - } - } - break; - case x86_64::kPointerDiff32: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) - throw "32-bit pointer difference out of range"; - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)displacement); - break; - case x86_64::kPointerDiff: - LittleEndian::set64(*fixUp, - (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case x86_64::kPointerDiff24: - displacement = (ref->getTarget().getAddress() + ref->getTargetOffset()) - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*((uint32_t*)fixUp)); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*((uint32_t*)fixUp), temp); - break; - case x86_64::kSectionOffset24: - displacement = ref->getTarget().getSectionOffset(); - if ( (displacement > kSixteenMegLimit) || (displacement < 0) ) - throwf("24-bit pointer diff out of range in %s", inAtom->getDisplayName()); - temp = LittleEndian::get32(*((uint32_t*)fixUp)); - temp &= 0xFF000000; - temp |= (displacement & 0x00FFFFFF); - LittleEndian::set32(*((uint32_t*)fixUp), temp); - break; - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - // if GOT entry was optimized away, change movq instruction to a leaq - if ( std::find(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), &(ref->getTarget())) == fAllSynthesizedNonLazyPointers.end() ) { - //fprintf(stderr, "GOT for %s optimized away\n", ref->getTarget().getDisplayName()); - uint8_t* opcodes = (uint8_t*)fixUp; - if ( opcodes[-2] != 0x8B ) - throw "GOT load reloc does not point to a movq instruction"; - opcodes[-2] = 0x8D; - } - // fall into general rel32 case - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel8: - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kTentativeDefinition: - displacement = (ref->getTarget().getAddress() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - displacement = (ref->getTarget().getSectionOffset() + (int32_t)ref->getTargetOffset()) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - if ( fOptions.outputKind() == Options::kKextBundle ) - displacement = 0; - else - throwf("codegen problem, can't use rel32 to external symbol %s", ref->getTarget().getDisplayName()); - break; - } - switch ( ref->getKind() ) { - case x86_64::kPCRel32_1: - displacement -= 1; - break; - case x86_64::kPCRel32_2: - displacement -= 2; - break; - case x86_64::kPCRel32_4: - displacement -= 4; - break; - case x86_64::kBranchPCRel8: - displacement += 3; - break; - } - if ( ref->getKind() == x86_64::kBranchPCRel8 ) { - if ( (displacement > 127) || (displacement < (-128)) ) { - fprintf(stderr, "branch out of range from %s (%llX) in %s to %s (%llX) in %s\n", - inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); - throw "rel8 out of range"; - } - *((int8_t*)fixUp) = (int8_t)displacement; - } - else { - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { - fprintf(stderr, "reference out of range from %s (%llX) in %s to %s (%llX) in %s\n", - inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getFile()->getPath(), ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getFile()->getPath()); - throw "rel32 out of range"; - } - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - } - break; - case x86_64::kImageOffset32: - // offset of target atom from mach_header - displacement = ref->getTarget().getAddress() + ref->getTargetOffset() - fMachHeaderAtom->getAddress(); - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - break; - case x86_64::kDtraceProbeSite: - // change call site to a NOP - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x90; // 1-byte nop - dtraceProbeSite[0] = 0x0F; // 4-byte nop - dtraceProbeSite[1] = 0x1F; - dtraceProbeSite[2] = 0x40; - dtraceProbeSite[3] = 0x00; - break; - case x86_64::kDtraceIsEnabledSite: - // change call site to a clear eax - dtraceProbeSite = (uint8_t*)fixUp; - dtraceProbeSite[-1] = 0x48; // xorq eax,eax - dtraceProbeSite[0] = 0x33; - dtraceProbeSite[1] = 0xC0; - dtraceProbeSite[2] = 0x90; // 1-byte nop - dtraceProbeSite[3] = 0x90; // 1-byte nop - break; - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -void Writer<x86_64>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - const int64_t twoGigLimit = 0x7FFFFFFF; - bool external = this->makesExternalRelocatableReference(ref->getTarget()); - uint64_t* fixUp = (uint64_t*)&buffer[ref->getFixUpOffset()]; - int64_t displacement = 0; - int32_t temp32; - switch ( (x86_64::ReferenceKinds)(ref->getKind()) ) { - case x86_64::kNoFixUp: - case x86_64::kGOTNoFixUp: - case x86_64::kFollowOn: - case x86_64::kGroupSubordinate: - // do nothing - break; - case x86_64::kPointer: - case x86_64::kPointerWeakImport: - { - if ( external ) { - // external relocation ==> pointer contains addend - LittleEndian::set64(*fixUp, ref->getTargetOffset()); - } - else { - // internal relocation ==> pointer contains target address - LittleEndian::set64(*fixUp, ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86_64::kPointer32: - { - if ( external ) { - // external relocation ==> pointer contains addend - LittleEndian::set32(*((uint32_t*)fixUp), ref->getTargetOffset()); - } - else { - // internal relocation ==> pointer contains target address - LittleEndian::set32(*((uint32_t*)fixUp), ref->getTarget().getAddress() + ref->getTargetOffset()); - } - } - break; - case x86_64::kPointerDiff32: - displacement = ref->getTargetOffset() - ref->getFromTargetOffset(); - if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement += ref->getTarget().getAddress(); - if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement -= ref->getFromTarget().getAddress(); - LittleEndian::set32(*((uint32_t*)fixUp), displacement); - break; - case x86_64::kPointerDiff: - displacement = ref->getTargetOffset() - ref->getFromTargetOffset(); - if ( ref->getTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement += ref->getTarget().getAddress(); - if ( ref->getFromTarget().getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - displacement -= ref->getFromTarget().getAddress(); - LittleEndian::set64(*fixUp, displacement); - break; - case x86_64::kBranchPCRel32: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kDtraceProbeSite: - case x86_64::kDtraceIsEnabledSite: - case x86_64::kPCRel32: - case x86_64::kPCRel32_1: - case x86_64::kPCRel32_2: - case x86_64::kPCRel32_4: - // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had - temp32 = ref->getTargetOffset(); - if ( external ) { - // extern relocation contains addend - displacement = temp32; - } - else { - // internal relocations contain delta to target address - displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 4); - } - switch ( ref->getKind() ) { - case x86_64::kPCRel32_1: - displacement -= 1; - break; - case x86_64::kPCRel32_2: - displacement -= 2; - break; - case x86_64::kPCRel32_4: - displacement -= 4; - break; - } - if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "rel32 out of range"; - } - LittleEndian::set32(*((uint32_t*)fixUp), (int32_t)displacement); - break; - case x86_64::kBranchPCRel8: - // turn unsigned 64-bit target offset in signed 32-bit offset, since that is what source originally had - temp32 = ref->getTargetOffset(); - if ( external ) { - // extern relocation contains addend - displacement = temp32; - } - else { - // internal relocations contain delta to target address - displacement = (ref->getTarget().getAddress() + temp32) - (inAtom->getAddress() + ref->getFixUpOffset() + 1); - } - if ( (displacement > 127) || (displacement < (-128)) ) { - //fprintf(stderr, "call out of range from %s in %s to %s in %s\n", this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throw "rel8 out of range"; - } - *((int8_t*)fixUp) = (int8_t)displacement; - break; - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoadWeakImport: - // contains addend (usually zero) - LittleEndian::set32(*((uint32_t*)fixUp), (uint32_t)(ref->getTargetOffset())); - break; - case x86_64::kPointerDiff24: - throw "internal linker error, kPointerDiff24 can't be encoded into object files"; - case x86_64::kImageOffset32: - throw "internal linker error, kImageOffset32 can't be encoded into object files"; - case x86_64::kSectionOffset24: - throw "internal linker error, kSectionOffset24 can't be encoded into object files"; - case x86_64::kDtraceTypeReference: - case x86_64::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -void Writer<ppc>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, true); -} - -template <> -void Writer<ppc64>::fixUpReferenceFinal(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, true); -} - -template <> -void Writer<ppc>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, false); -} - -template <> -void Writer<ppc64>::fixUpReferenceRelocatable(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[]) const -{ - fixUpReference_powerpc(ref, inAtom, buffer, false); -} - -// -// ppc and ppc64 are mostly the same, so they share a template specialzation -// -template <typename A> -void Writer<A>::fixUpReference_powerpc(const ObjectFile::Reference* ref, const ObjectFile::Atom* inAtom, uint8_t buffer[], bool finalLinkedImage) const -{ - uint32_t instruction; - uint32_t newInstruction; - int64_t displacement; - uint64_t targetAddr = 0; - uint64_t picBaseAddr; - uint16_t instructionLowHalf; - uint16_t instructionHighHalf; - uint32_t* fixUp = (uint32_t*)&buffer[ref->getFixUpOffset()]; - pint_t* fixUpPointer = (pint_t*)&buffer[ref->getFixUpOffset()]; - bool relocateableExternal = false; - const int64_t picbase_twoGigLimit = 0x80000000; - - if ( ref->getTargetBinding() != ObjectFile::Reference::kDontBind ) { - targetAddr = ref->getTarget().getAddress() + ref->getTargetOffset(); - if ( finalLinkedImage ) - relocateableExternal = (relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal); - else - relocateableExternal = this->makesExternalRelocatableReference(ref->getTarget()); - } - - switch ( (typename A::ReferenceKinds)(ref->getKind()) ) { - case A::kNoFixUp: - case A::kFollowOn: - case A::kGroupSubordinate: - // do nothing - break; - case A::kPointerWeakImport: - case A::kPointer: - { - //fprintf(stderr, "fixUpReferenceFinal: %s reference to %s\n", this->getDisplayName(), target.getDisplayName()); - if ( finalLinkedImage && (((SectionInfo*)inAtom->getSection())->fAllLazyPointers - || ((SectionInfo*)inAtom->getSection())->fAllLazyDylibPointers) ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound lazy pointer to another dylib ==> pointer contains zero - P::setP(*fixUpPointer, 0); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - // prebound lazy pointer to withing this dylib ==> pointer contains address - P::setP(*fixUpPointer, targetAddr); - break; - } - } - else if ( !finalLinkedImage && ((SectionInfo*)inAtom->getSection())->fAllNonLazyPointers ) { - // if INDIRECT_SYMBOL_LOCAL the content is pointer, else it is zero - if ( this->indirectSymbolInRelocatableIsLocal(ref) ) - P::setP(*fixUpPointer, targetAddr); - else - P::setP(*fixUpPointer, 0); - } - else if ( relocateableExternal ) { - if ( fOptions.prebind() ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // prebound external relocation ==> pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // prebound external relocation to internal atom ==> pointer contains target address + addend - P::setP(*fixUpPointer, targetAddr); - break; - case ObjectFile::Atom::kAbsoluteSymbol: - break; - } - } - else { - // external relocation ==> pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - } - } - else { - // internal relocation - if ( finalLinkedImage || (ref->getTarget().getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { - // pointer contains target address - //printf("Atom::fixUpReference_powerpc() target.name=%s, target.address=0x%08llX\n", ref->getTarget().getDisplayName(), targetAddr); - P::setP(*fixUpPointer, targetAddr); - } - else { - // pointer contains addend - P::setP(*fixUpPointer, ref->getTargetOffset()); - } - } - } - break; - case A::kPointerDiff64: - P::setP(*fixUpPointer, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kPointerDiff32: - P::E::set32(*fixUp, targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kPointerDiff16: - P::E::set16(*((uint16_t*)fixUp), targetAddr - (ref->getFromTarget().getAddress() + ref->getFromTargetOffset()) ); - break; - case A::kDtraceProbeSite: - if ( finalLinkedImage ) { - // change call site to a NOP - BigEndian::set32(*fixUp, 0x60000000); - } - else { - // set bl instuction to branch to address zero in .o file - int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kDtraceIsEnabledSite: - if ( finalLinkedImage ) { - // change call site to a li r3,0 - BigEndian::set32(*fixUp, 0x38600000); - } - else { - // set bl instuction to branch to address zero in .o file - int64_t displacement = ref->getTargetOffset() - (inAtom->getAddress() + ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kBranch24WeakImport: - case A::kBranch24: - { - //fprintf(stderr, "bl fixup to %s at 0x%08llX, ", target.getDisplayName(), target.getAddress()); - int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - else { - const int64_t bl_eightMegLimit = 0x00FFFFFF; - if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { - //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("bl out of range (%lld max is +/-16M) from %s at 0x%08llX in %s of %s to %s at 0x%08llX in %s of %s", - displacement, inAtom->getDisplayName(), inAtom->getAddress(), inAtom->getSectionName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getAddress(), ref->getTarget().getSectionName(), ref->getTarget().getFile()->getPath()); - } - } - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFC000003) | ((uint32_t)displacement & 0x03FFFFFC); - //fprintf(stderr, "bl fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kBranch14: - { - int64_t displacement = targetAddr - (inAtom->getAddress() + ref->getFixUpOffset()); - if ( relocateableExternal ) { - // doing "ld -r" to an external symbol - // the mach-o way of encoding this is that the bl instruction's target addr is the offset into the target - displacement -= ref->getTarget().getAddress(); - } - const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; - if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { - //fprintf(stderr, "bl out of range (%lld max is +/-16M) from %s in %s to %s in %s\n", displacement, this->getDisplayName(), this->getFile()->getPath(), target.getDisplayName(), target.getFile()->getPath()); - throwf("bcc out of range (%lld max is +/-64K) from %s in %s to %s in %s", - displacement, inAtom->getDisplayName(), inAtom->getFile()->getPath(), - ref->getTarget().getDisplayName(), ref->getTarget().getFile()->getPath()); - } - - //fprintf(stderr, "bcc fixup displacement=0x%08llX, atom.addr=0x%08llX, atom.offset=0x%08X\n", displacement, inAtom->getAddress(), (uint32_t)ref->getFixUpOffset()); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)displacement & 0x0000FFFC); - //fprintf(stderr, "bc fixup: 0x%08X -> 0x%08X\n", instruction, newInstruction); - BigEndian::set32(*fixUp, newInstruction); - } - break; - case A::kPICBaseLow16: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - instructionLowHalf = (displacement & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kPICBaseLow14: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - if ( (displacement & 0x3) != 0 ) - throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)displacement); - instructionLowHalf = (displacement & 0xFFFC); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kPICBaseHigh16: - picBaseAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset(); - displacement = targetAddr - picBaseAddr; - if ( (displacement > picbase_twoGigLimit) || (displacement < (-picbase_twoGigLimit)) ) - throw "32-bit pic-base out of range"; - instructionLowHalf = displacement >> 16; - if ( (displacement & 0x00008000) != 0 ) - ++instructionLowHalf; - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsLow16: - if ( relocateableExternal && !finalLinkedImage ) - targetAddr -= ref->getTarget().getAddress(); - instructionLowHalf = (targetAddr & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsLow14: - if ( relocateableExternal && !finalLinkedImage ) - targetAddr -= ref->getTarget().getAddress(); - if ( (targetAddr & 0x3) != 0 ) - throw "bad address for absolute lo14 instruction fix-up"; - instructionLowHalf = (targetAddr & 0xFFFF); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0003) | instructionLowHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsHigh16: - if ( relocateableExternal ) { - if ( finalLinkedImage ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // use target address - break; - case ObjectFile::Atom::kAbsoluteSymbol: - targetAddr = ref->getTarget().getSectionOffset(); - break; - } - } - else { - targetAddr -= ref->getTarget().getAddress(); - } - } - instructionHighHalf = (targetAddr >> 16); - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | instructionHighHalf; - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kAbsHigh16AddLow: - if ( relocateableExternal ) { - if ( finalLinkedImage ) { - switch (ref->getTarget().getDefinitionKind()) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - throwf("absolute address to symbol %s in a different linkage unit not supported", ref->getTargetName()); - break; - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - // use target address - break; - case ObjectFile::Atom::kAbsoluteSymbol: - targetAddr = ref->getTarget().getSectionOffset(); - break; - } - } - else { - targetAddr -= ref->getTarget().getAddress(); - } - } - if ( targetAddr & 0x00008000 ) - targetAddr += 0x00010000; - instruction = BigEndian::get32(*fixUp); - newInstruction = (instruction & 0xFFFF0000) | (targetAddr >> 16); - BigEndian::set32(*fixUp, newInstruction); - break; - case A::kDtraceTypeReference: - case A::kDtraceProbe: - // nothing to fix up - break; - } -} - -template <> -bool Writer<ppc>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (ppc::ReferenceKinds)kind ) { - case ppc::kNoFixUp: - case ppc::kFollowOn: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPointerDiff16: - case ppc::kPointerDiff32: - case ppc::kPointerDiff64: - case ppc::kDtraceProbe: - case ppc::kDtraceProbeSite: - case ppc::kDtraceIsEnabledSite: - case ppc::kDtraceTypeReference: - // these are never used to call external functions - return false; - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - case ppc::kBranch14: - // these are used to call external functions - return true; - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - case ppc::kPICBaseHigh16: - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - // these are only used to call external functions - // in -mlong-branch stubs - switch ( ref->getTarget().getDefinitionKind() ) { - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - // if the .o file this atom came from has long-branch stubs, - // then assume these instructions in a stub. - // Otherwise, these are a direct reference to something (maybe a runtime text reloc) - return ( inAtom->getFile()->hasLongBranchStubs() ); - case ObjectFile::Atom::kTentativeDefinition: - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - return false; - } - break; - } - return false; -} - -template <> -bool Writer<arm>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (arm::ReferenceKinds)kind ) { - case arm::kBranch24: - case arm::kBranch24WeakImport: - return true; - case arm::kThumbBranch22: - case arm::kThumbBranch22WeakImport: - fHasThumbBranches = true; - return true; - case arm::kNoFixUp: - case arm::kFollowOn: - case arm::kGroupSubordinate: - case arm::kPointer: - case arm::kReadOnlyPointer: - case arm::kPointerWeakImport: - case arm::kPointerDiff: - case arm::kDtraceProbe: - case arm::kDtraceProbeSite: - case arm::kDtraceIsEnabledSite: - case arm::kDtraceTypeReference: - case arm::kPointerDiff12: - return false; - } - return false; -} - -template <> -bool Writer<ppc64>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - switch ( (ppc64::ReferenceKinds)kind ) { - case ppc::kNoFixUp: - case ppc::kFollowOn: - case ppc::kGroupSubordinate: - case ppc::kPointer: - case ppc::kPointerWeakImport: - case ppc::kPointerDiff16: - case ppc::kPointerDiff32: - case ppc::kPointerDiff64: - case ppc::kPICBaseLow16: - case ppc::kPICBaseLow14: - case ppc::kPICBaseHigh16: - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - case ppc::kDtraceProbe: - case ppc::kDtraceProbeSite: - case ppc::kDtraceIsEnabledSite: - case ppc::kDtraceTypeReference: - // these are never used to call external functions - return false; - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - case ppc::kBranch14: - // these are used to call external functions - return true; - } - return false; -} - -template <> -bool Writer<x86>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - return (kind == x86::kPCRel32 || kind == x86::kPCRel32WeakImport); -} - -template <> -bool Writer<x86_64>::stubableReference(const ObjectFile::Atom* inAtom, const ObjectFile::Reference* ref) -{ - uint8_t kind = ref->getKind(); - return (kind == x86_64::kBranchPCRel32 || kind == x86_64::kBranchPCRel32WeakImport); -} - - -template <> -bool Writer<ppc>::weakImportReferenceKind(uint8_t kind) -{ - return (kind == ppc::kBranch24WeakImport || kind == ppc::kPointerWeakImport); -} - -template <> -bool Writer<ppc64>::weakImportReferenceKind(uint8_t kind) -{ - return (kind == ppc64::kBranch24WeakImport || kind == ppc64::kPointerWeakImport); -} - -template <> -bool Writer<x86>::weakImportReferenceKind(uint8_t kind) -{ - return (kind == x86::kPCRel32WeakImport || kind == x86::kPointerWeakImport); -} - -template <> -bool Writer<x86_64>::weakImportReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPointerWeakImport: - case x86_64::kBranchPCRel32WeakImport: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoadWeakImport: - return true; - } - return false; -} - -template <> -bool Writer<arm>::weakImportReferenceKind(uint8_t kind) -{ - return (kind == arm::kBranch24WeakImport || kind == arm::kThumbBranch22WeakImport || - kind == arm::kPointerWeakImport); -} - -template <> -bool Writer<ppc>::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer<ppc64>::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer<x86>::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer<x86_64>::GOTReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPCRel32GOT: - case x86_64::kPCRel32GOTWeakImport: - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - case x86_64::kGOTNoFixUp: - return true; - } - return false; -} - -template <> -bool Writer<arm>::GOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer<ppc>::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer<ppc64>::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer<x86>::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -template <> -bool Writer<x86_64>::optimizableGOTReferenceKind(uint8_t kind) -{ - switch ( kind ) { - case x86_64::kPCRel32GOTLoad: - case x86_64::kPCRel32GOTLoadWeakImport: - return true; - } - return false; -} - -template <> -bool Writer<arm>::optimizableGOTReferenceKind(uint8_t kind) -{ - return false; -} - -// 64-bit architectures never need module table, 32-bit sometimes do for backwards compatiblity -template <typename A> bool Writer<A>::needsModuleTable() {return fOptions.needsModuleTable(); } -template <> bool Writer<ppc64>::needsModuleTable() { return false; } -template <> bool Writer<x86_64>::needsModuleTable() { return false; } - - -template <typename A> -void Writer<A>::optimizeDylibReferences() -{ - //fprintf(stderr, "original ordinals table:\n"); - //for (std::map<class ObjectFile::Reader*, uint32_t>::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<uint32_t, ObjectFile::Reader*> ordinalToReader; - std::map<ObjectFile::Reader*, ObjectFile::Reader*> readerAliases; - for (std::map<ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - ObjectFile::Reader* reader = it->first; - std::map<ObjectFile::Reader*, ObjectFile::Reader*>::iterator aliasPos = fLibraryAliases.find(reader); - if ( aliasPos != fLibraryAliases.end() ) { - // already noticed that this reader has same install name as another reader - readerAliases[reader] = aliasPos->second; - } - else if ( !reader->providedExportAtom() && (reader->implicitlyLinked() || reader->deadStrippable() || fOptions.deadStripDylibs()) ) { - // this reader can be optimized away - it->second = 0xFFFFFFFF; - typename std::map<class ObjectFile::Reader*, class DylibLoadCommandsAtom<A>* >::iterator pos = fLibraryToLoadCommand.find(reader); - if ( pos != fLibraryToLoadCommand.end() ) - pos->second->optimizeAway(); - } - else { - // mark this reader as using it ordinal - std::map<uint32_t, ObjectFile::Reader*>::iterator pos = ordinalToReader.find(it->second); - if ( pos == ordinalToReader.end() ) - ordinalToReader[it->second] = reader; - else - readerAliases[reader] = pos->second; - } - } - // renumber ordinals (depends on iterator walking in ordinal order) - // all LC_LAZY_LOAD_DYLIB load commands must have highest ordinals - uint32_t newOrdinal = 0; - for (std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first <= fLibraryToOrdinal.size() ) { - if ( ! it->second->isLazyLoadedDylib() ) - fLibraryToOrdinal[it->second] = ++newOrdinal; - } - } - for (std::map<uint32_t, ObjectFile::Reader*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { - if ( it->first <= fLibraryToOrdinal.size() ) { - if ( it->second->isLazyLoadedDylib() ) { - fLibraryToOrdinal[it->second] = ++newOrdinal; - } - } - } - - // <rdar://problem/5504954> linker does not error when dylib ordinal exceeds 250 - if ( (newOrdinal >= MAX_LIBRARY_ORDINAL) && (fOptions.nameSpace() == Options::kTwoLevelNameSpace) ) - throwf("two level namespace mach-o files can link with at most %d dylibs, this link would use %d dylibs", MAX_LIBRARY_ORDINAL, newOrdinal); - - // add aliases (e.g. -lm points to libSystem.dylib) - for (std::map<ObjectFile::Reader*, ObjectFile::Reader*>::iterator it = readerAliases.begin(); it != readerAliases.end(); ++it) { - fLibraryToOrdinal[it->first] = fLibraryToOrdinal[it->second]; - } - - //fprintf(stderr, "new ordinals table:\n"); - //for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) { - // fprintf(stderr, "%u <== %p/%s\n", it->second, it->first, it->first->getPath()); - //} -} - - -template <> -void Writer<arm>::scanForAbsoluteReferences() -{ - // arm codegen never has absolute references. FIXME: Is this correct? -} - -template <> -void Writer<x86_64>::scanForAbsoluteReferences() -{ - // x86_64 codegen never has absolute references -} - -template <> -void Writer<x86>::scanForAbsoluteReferences() -{ - // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used - if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getContentType() == ObjectFile::Atom::kStub ) - continue; - if ( atom->getContentType() == ObjectFile::Atom::kStubHelper ) - continue; - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case x86::kAbsolute32: - throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); - return; - } - } - } - } -} - -template <> -void Writer<ppc>::scanForAbsoluteReferences() -{ - // when linking -pie verify there are no absolute addressing, unless -read_only_relocs is also used - if ( fOptions.positionIndependentExecutable() && !fOptions.allowTextRelocs() ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case ppc::kAbsLow16: - case ppc::kAbsLow14: - case ppc::kAbsHigh16: - case ppc::kAbsHigh16AddLow: - throwf("cannot link -pie: -mdynamic-no-pic codegen found in %s from %s", atom->getDisplayName(), atom->getFile()->getPath()); - return; - } - } - } - } -} - - -// for ppc64 look for any -mdynamic-no-pic codegen -template <> -void Writer<ppc64>::scanForAbsoluteReferences() -{ - // only do this for main executable - if ( mightNeedPadSegment() && (fPageZeroAtom != NULL) ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch (ref->getKind()) { - case ppc64::kAbsLow16: - case ppc64::kAbsLow14: - case ppc64::kAbsHigh16: - case ppc64::kAbsHigh16AddLow: - //fprintf(stderr, "found -mdynamic-no-pic codegen in %s in %s\n", atom->getDisplayName(), atom->getFile()->getPath()); - // shrink page-zero and add pad segment to compensate - fPadSegmentInfo = new SegmentInfo(4096); - strcpy(fPadSegmentInfo->fName, "__4GBFILL"); - fPageZeroAtom->setSize(0x1000); - return; - } - } - } - } -} - - -template <typename A> -void Writer<A>::insertDummyStubs() -{ - // only needed for x86 -} - -template <> -void Writer<x86>::insertDummyStubs() -{ - // any 5-byte stubs that cross a 32-byte cache line may update incorrectly - std::vector<class StubAtom<x86>*> betterStubs; - for (std::vector<class StubAtom<x86>*>::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<x86>(*this, *((ObjectFile::Atom*)NULL), false)); //pad with dummy stub - break; - } - betterStubs.push_back(*it); - } - // replace - fAllSynthesizedStubs.clear(); - fAllSynthesizedStubs.insert(fAllSynthesizedStubs.begin(), betterStubs.begin(), betterStubs.end()); -} - - -template <typename A> -void Writer<A>::synthesizeKextGOT(const std::vector<class ObjectFile::Atom*>& existingAtoms, - std::vector<class ObjectFile::Atom*>& newAtoms) -{ - // walk every atom and reference - for (std::vector<ObjectFile::Atom*>::const_iterator it=existingAtoms.begin(); it != existingAtoms.end(); it++) { - const ObjectFile::Atom* atom = *it; - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - ObjectFile::Atom& target = ref->getTarget(); - // create GOT slots (non-lazy pointers) as needed - if ( this->GOTReferenceKind(ref->getKind()) ) { - bool useGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); - // if this GOT usage cannot be optimized away then make a GOT enry - if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) - useGOT = true; - if ( useGOT ) { - ObjectFile::Atom* nlp = NULL; - std::map<ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fGOTMap.find(&target); - if ( pos == fGOTMap.end() ) { - nlp = new NonLazyPointerAtom<A>(*this, target); - fGOTMap[&target] = nlp; - newAtoms.push_back(nlp); - } - else { - nlp = pos->second; - } - // alter reference to use non lazy pointer instead - ref->setTarget(*nlp, ref->getTargetOffset()); - } - } - // build map of which symbols need weak importing - if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - if ( this->weakImportReferenceKind(ref->getKind()) ) { - fWeakImportMap[&target] = true; - } - } - break; - } - } - } -} - - -template <typename A> -void Writer<A>::synthesizeStubs(const std::vector<class ObjectFile::Atom*>& existingAtoms, - std::vector<class ObjectFile::Atom*>& newAtoms) -{ - switch ( fOptions.outputKind() ) { - case Options::kObjectFile: - case Options::kPreload: - // these output kinds never have stubs - return; - case Options::kKextBundle: - // new kext need a synthesized GOT only - synthesizeKextGOT(existingAtoms, newAtoms); - return; - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDynamicExecutable: - // try to synthesize stubs for these - break; - } - - // walk every atom and reference - for (std::vector<ObjectFile::Atom*>::const_iterator it=existingAtoms.begin(); it != existingAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - ObjectFile::Atom& target = ref->getTarget(); - // build map of which symbols need weak importing - if ( (target.getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (target.getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - bool weakImport = this->weakImportReferenceKind(ref->getKind()); - // <rdar://problem/5633081> Obj-C Symbols in Leopard Can't Be Weak Linked - // dyld in Mac OS X 10.3 and earlier need N_WEAK_REF bit set on undefines to objc symbols - // in dylibs that are weakly linked. - if ( (ref->getKind() == A::kNoFixUp) && (strncmp(target.getName(), ".objc_class_name_", 17) == 0) ) { - typename std::map<class ObjectFile::Reader*, class DylibLoadCommandsAtom<A>* >::iterator pos; - pos = fLibraryToLoadCommand.find(target.getFile()); - if ( pos != fLibraryToLoadCommand.end() ) { - if ( pos->second->linkedWeak() ) - weakImport = true; - } - } - // <rdar://problem/6186838> -weak_library no longer forces uses to be weak_import - if ( fForcedWeakImportReaders.count(target.getFile()) != 0 ) { - fWeakImportMap[&target] = true; - weakImport = true; - } - - std::map<const ObjectFile::Atom*,bool>::iterator pos = fWeakImportMap.find(&target); - if ( pos == fWeakImportMap.end() ) { - // target not in fWeakImportMap, so add - fWeakImportMap[&target] = weakImport; - } - else { - // target in fWeakImportMap, check for weakness mismatch - if ( pos->second != weakImport ) { - // found mismatch - switch ( fOptions.weakReferenceMismatchTreatment() ) { - case Options::kWeakReferenceMismatchError: - throwf("mismatching weak references for symbol: %s", target.getName()); - case Options::kWeakReferenceMismatchWeak: - pos->second = true; - break; - case Options::kWeakReferenceMismatchNonWeak: - pos->second = false; - break; - } - } - } - // update if we use a weak_import or a strong import from this dylib - if ( fWeakImportMap[&target] ) - fDylibReadersWithWeakImports.insert(target.getFile()); - else - fDylibReadersWithNonWeakImports.insert(target.getFile()); - } - // create stubs as needed - if ( this->stubableReference(atom, ref) - && (ref->getTargetOffset() == 0) - && this->relocationNeededInFinalLinkedImage(target) == kRelocExternal ) { - ObjectFile::Atom* stub = NULL; - std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fStubsMap.find(&target); - if ( pos == fStubsMap.end() ) { - bool forLazyDylib = false; - switch ( target.getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: - case ObjectFile::Atom::kWeakDefinition: - case ObjectFile::Atom::kAbsoluteSymbol: - case ObjectFile::Atom::kTentativeDefinition: - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - if ( target.getFile()->isLazyLoadedDylib() ) - forLazyDylib = true; - break; - } - // just-in-time, create GOT slot to dyld_stub_binder - if ( fOptions.makeCompressedDyldInfo() && (fFastStubGOTAtom == NULL) ) { - if ( fDyldCompressedHelperAtom == NULL ) - throw "missing symbol dyld_stub_binder"; - fFastStubGOTAtom = new NonLazyPointerAtom<A>(*this, *fDyldCompressedHelperAtom); - } - stub = new StubAtom<A>(*this, target, forLazyDylib); - fStubsMap[&target] = stub; - } - else { - stub = pos->second; - } - // alter reference to use stub instead - ref->setTarget(*stub, 0); - } - else if ( fOptions.usingLazyDylibLinking() && target.getFile()->isLazyLoadedDylib() ) { - throwf("illegal reference to %s in lazy loaded dylib from %s in %s", - target.getDisplayName(), atom->getDisplayName(), - atom->getFile()->getPath()); - } - // create GOT slots (non-lazy pointers) as needed - else if ( this->GOTReferenceKind(ref->getKind()) ) { - // - bool mustUseGOT = ( this->relocationNeededInFinalLinkedImage(ref->getTarget()) == kRelocExternal ); - bool useGOT; - if ( fBiggerThanTwoGigs ) { - // in big images use GOT for all zero fill atoms - // this is just a heuristic and may need to be re-examined - useGOT = mustUseGOT || ref->getTarget().isZeroFill(); - } - else { - // < 2GB image so remove all GOT entries that we can - useGOT = mustUseGOT; - } - // if this GOT usage cannot be optimized away then make a GOT enry - if ( ! this->optimizableGOTReferenceKind(ref->getKind()) ) - useGOT = true; - if ( useGOT ) { - ObjectFile::Atom* nlp = NULL; - std::map<ObjectFile::Atom*,ObjectFile::Atom*>::iterator pos = fGOTMap.find(&target); - if ( pos == fGOTMap.end() ) { - nlp = new NonLazyPointerAtom<A>(*this, target); - fGOTMap[&target] = nlp; - } - else { - nlp = pos->second; - } - // alter reference to use non lazy pointer instead - ref->setTarget(*nlp, ref->getTargetOffset()); - } - } - } - } - } - - // sort stubs - std::sort(fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end(), AtomByNameSorter()); - // add dummy self-modifying stubs (x86 only) - if ( ! fOptions.makeCompressedDyldInfo() ) - this->insertDummyStubs(); - // set ordinals so sorting is preserved - uint32_t sortOrder = 0; - for (typename std::vector<StubAtom<A>*>::iterator it=fAllSynthesizedStubs.begin(); it != fAllSynthesizedStubs.end(); it++) - (*it)->setSortingOrdinal(sortOrder++); - std::sort(fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end(), AtomByNameSorter()); - - // sort lazy pointers - std::sort(fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end(), AtomByNameSorter()); - sortOrder = 0; - for (typename std::vector<LazyPointerAtom<A>*>::iterator it=fAllSynthesizedLazyPointers.begin(); it != fAllSynthesizedLazyPointers.end(); it++) - (*it)->setSortingOrdinal(sortOrder++); - std::sort(fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end(), AtomByNameSorter()); - - // sort non-lazy pointers - std::sort(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), AtomByNameSorter()); - sortOrder = 0; - for (typename std::vector<NonLazyPointerAtom<A>*>::iterator it=fAllSynthesizedNonLazyPointers.begin(); it != fAllSynthesizedNonLazyPointers.end(); it++) - (*it)->setSortingOrdinal(sortOrder++); - std::sort(fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end(), AtomByNameSorter()); - - // tell linker about all synthesized atoms - newAtoms.insert(newAtoms.end(), fAllSynthesizedStubs.begin(), fAllSynthesizedStubs.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedStubHelpers.begin(), fAllSynthesizedStubHelpers.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedLazyPointers.begin(), fAllSynthesizedLazyPointers.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedLazyDylibPointers.begin(), fAllSynthesizedLazyDylibPointers.end()); - newAtoms.insert(newAtoms.end(), fAllSynthesizedNonLazyPointers.begin(), fAllSynthesizedNonLazyPointers.end()); - -} - -template <typename A> -void Writer<A>::createSplitSegContent() -{ - // build LC_SEGMENT_SPLIT_INFO once all atoms exist - if ( fSplitCodeToDataContentAtom != NULL ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - switch ( ref->getTargetBinding()) { - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - break; - case ObjectFile::Reference::kBoundByName: - case ObjectFile::Reference::kBoundDirectly: - if ( this->segmentsCanSplitApart(*atom, ref->getTarget()) ) { - this->addCrossSegmentRef(atom, ref); - } - break; - } - } - } - // bad codegen may cause LC_SEGMENT_SPLIT_INFO to be removed - adjustLoadCommandsAndPadding(); - } - -} - - -template <typename A> -void Writer<A>::synthesizeUnwindInfoTable() -{ - if ( fUnwindInfoAtom != NULL ) { - // walk every atom and gets its unwind info - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->beginUnwind() == atom->endUnwind() ) { - // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info - if ( strcmp(atom->getSegment().getName(), "__TEXT") == 0 ) - fUnwindInfoAtom->addUnwindInfo(atom, 0, 0, NULL, NULL, NULL); - } - else { - // atom has unwind - for ( ObjectFile::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) { - fUnwindInfoAtom->addUnwindInfo(atom, uit->startOffset, uit->unwindInfo, atom->getFDE(), atom->getLSDA(), atom->getPersonalityPointer()); - } - } - } - } -} - - -template <typename A> -void Writer<A>::partitionIntoSections() -{ - const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile); - - // for every atom, set its sectionInfo object and section offset - // build up fSegmentInfos along the way - ObjectFile::Section* curSection = (ObjectFile::Section*)(-1); - SectionInfo* currentSectionInfo = NULL; - SegmentInfo* currentSegmentInfo = NULL; - SectionInfo* cstringSectionInfo = NULL; - unsigned int sectionIndex = 1; - fSegmentInfos.reserve(8); - for (unsigned int i=0; i < fAllAtoms->size(); ++i) { - ObjectFile::Atom* atom = (*fAllAtoms)[i]; - if ( ((atom->getSection() != curSection) || (curSection==NULL)) - && ((currentSectionInfo == NULL) - || (strcmp(atom->getSectionName(),currentSectionInfo->fSectionName) != 0) - || (strcmp(atom->getSegment().getName(),currentSectionInfo->fSegmentName) != 0)) ) { - if ( oneSegmentCommand ) { - if ( currentSegmentInfo == NULL ) { - currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment()); - currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - this->fSegmentInfos.push_back(currentSegmentInfo); - } - currentSectionInfo = new SectionInfo(); - strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); - strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; - currentSectionInfo->fAllZeroFill = atom->isZeroFill(); - currentSectionInfo->fVirtualSection = (currentSectionInfo->fSectionName[0] == '.'); - if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) - currentSectionInfo->setIndex(sectionIndex++); - currentSegmentInfo->fSections.push_back(currentSectionInfo); - if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__cstring") == 0) ) - cstringSectionInfo = currentSectionInfo; - } - else { - if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) { - currentSegmentInfo = new SegmentInfo(fOptions.segmentAlignment()); - strcpy(currentSegmentInfo->fName, atom->getSegment().getName()); - uint32_t initprot = 0; - if ( atom->getSegment().isContentReadable() ) - initprot |= VM_PROT_READ; - if ( atom->getSegment().isContentWritable() ) - initprot |= VM_PROT_WRITE; - if ( atom->getSegment().isContentExecutable() ) - initprot |= VM_PROT_EXECUTE; - if ( fOptions.readOnlyx86Stubs() && (strcmp(atom->getSegment().getName(), "__IMPORT") == 0) ) - initprot &= ~VM_PROT_WRITE; // hack until i386 __pointers section is synthesized by linker - currentSegmentInfo->fInitProtection = initprot; - if ( initprot == 0 ) - currentSegmentInfo->fMaxProtection = 0; // pagezero should have maxprot==initprot==0 - else if ( fOptions.architecture() == CPU_TYPE_ARM ) - currentSegmentInfo->fMaxProtection = currentSegmentInfo->fInitProtection; // iPhoneOS wants max==init - else - currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - std::vector<Options::SegmentProtect>& customSegProtections = fOptions.customSegmentProtections(); - for(std::vector<Options::SegmentProtect>::iterator it = customSegProtections.begin(); it != customSegProtections.end(); ++it) { - if ( strcmp(it->name, currentSegmentInfo->fName) == 0 ) { - currentSegmentInfo->fInitProtection = it->init; - currentSegmentInfo->fMaxProtection = it->max; - } - } - currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress(); - currentSegmentInfo->fFixedAddress = atom->getSegment().hasFixedAddress(); - if ( currentSegmentInfo->fFixedAddress && (&(atom->getSegment()) == &Segment::fgStackSegment) ) - currentSegmentInfo->fIndependentAddress = true; - if ( (fOptions.outputKind() == Options::kPreload) && (strcmp(currentSegmentInfo->fName, "__LINKEDIT")==0) ) - currentSegmentInfo->fHasLoadCommand = false; - if ( strcmp(currentSegmentInfo->fName, "__HEADER")==0 ) - currentSegmentInfo->fHasLoadCommand = false; - this->fSegmentInfos.push_back(currentSegmentInfo); - } - currentSectionInfo = new SectionInfo(); - currentSectionInfo->fAtoms.reserve(fAllAtoms->size()/4); // reduce reallocations by starting large - strcpy(currentSectionInfo->fSectionName, atom->getSectionName()); - strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName()); - currentSectionInfo->fAlignment = atom->getAlignment().powerOf2; - // check for -sectalign override - std::vector<Options::SectionAlignment>& alignmentOverrides = fOptions.sectionAlignments(); - for(std::vector<Options::SectionAlignment>::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) { - if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) ) - currentSectionInfo->fAlignment = it->alignment; - } - currentSectionInfo->fAllZeroFill = atom->isZeroFill(); - currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.'); - if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections ) - currentSectionInfo->setIndex(sectionIndex++); - currentSegmentInfo->fSections.push_back(currentSectionInfo); - } - //fprintf(stderr, "new section %s for atom %s\n", atom->getSectionName(), atom->getDisplayName()); - if ( strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0 ) { - fLoadCommandsSection = currentSectionInfo; - fLoadCommandsSegment = currentSegmentInfo; - } - switch ( atom->getContentType() ) { - case ObjectFile::Atom::kLazyPointer: - currentSectionInfo->fAllLazyPointers = true; - fSymbolTableCommands->needDynamicTable(); - break; - case ObjectFile::Atom::kNonLazyPointer: - currentSectionInfo->fAllNonLazyPointers = true; - fSymbolTableCommands->needDynamicTable(); - break; - case ObjectFile::Atom::kLazyDylibPointer: - currentSectionInfo->fAllLazyDylibPointers = true; - break; - case ObjectFile::Atom::kStubHelper: - currentSectionInfo->fAllStubHelpers = true; - break; - case ObjectFile::Atom::kCFIType: - currentSectionInfo->fAlignment = __builtin_ctz(sizeof(pint_t)); // always start CFI info pointer aligned - break; - case ObjectFile::Atom::kStub: - if ( (strcmp(currentSectionInfo->fSegmentName, "__IMPORT") == 0) && (strcmp(currentSectionInfo->fSectionName, "__jump_table") == 0) ) { - currentSectionInfo->fAllSelfModifyingStubs = true; - currentSectionInfo->fAlignment = 6; // force x86 fast stubs to start on 64-byte boundary - } - else { - currentSectionInfo->fAllStubs = true; - } - fSymbolTableCommands->needDynamicTable(); - break; - default: - break; - } - curSection = atom->getSection(); - } - // any non-zero fill atoms make whole section marked not-zero-fill - if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() ) - currentSectionInfo->fAllZeroFill = false; - // change section object to be Writer's SectionInfo object - atom->setSection(currentSectionInfo); - // section alignment is that of a contained atom with the greatest alignment - uint8_t atomAlign = atom->getAlignment().powerOf2; - if ( currentSectionInfo->fAlignment < atomAlign ) - currentSectionInfo->fAlignment = atomAlign; - // calculate section offset for this atom - uint64_t offset = currentSectionInfo->fSize; - uint64_t alignment = 1 << atomAlign; - uint64_t currentModulus = (offset % alignment); - uint64_t requiredModulus = atom->getAlignment().modulus; - if ( currentModulus != requiredModulus ) { - if ( requiredModulus > currentModulus ) - offset += requiredModulus-currentModulus; - else - offset += requiredModulus+alignment-currentModulus; - } - atom->setSectionOffset(offset); - uint64_t curAtomSize = atom->getSize(); - currentSectionInfo->fSize = offset + curAtomSize; - // add atom to section vector - currentSectionInfo->fAtoms.push_back(atom); - //fprintf(stderr, " adding atom %p %s size=0x%0llX to section %p %s from %s\n", atom, atom->getDisplayName(), atom->getSize(), - // currentSectionInfo, currentSectionInfo->fSectionName, atom->getFile()->getPath()); - // update largest size - if ( !currentSectionInfo->fAllZeroFill && (curAtomSize > fLargestAtomSize) ) - fLargestAtomSize = curAtomSize; - } - if ( (cstringSectionInfo != NULL) && (cstringSectionInfo->fAlignment > 0) ) { - // when merging cstring sections in .o files, all strings need to use the max alignment - uint64_t offset = 0; - uint64_t cstringAlignment = 1 << cstringSectionInfo->fAlignment; - for (std::vector<ObjectFile::Atom*>::iterator it=cstringSectionInfo->fAtoms.begin(); it != cstringSectionInfo->fAtoms.end(); it++) { - offset = (offset + (cstringAlignment-1)) & (-cstringAlignment); - ObjectFile::Atom* atom = *it; - atom->setSectionOffset(offset); - offset += atom->getSize(); - } - cstringSectionInfo->fSize = offset; - } -} - - -struct TargetAndOffset { ObjectFile::Atom* atom; uint32_t offset; }; -class TargetAndOffsetComparor -{ -public: - bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const - { - if ( left.atom != right.atom ) - return ( left.atom < right.atom ); - return ( left.offset < right.offset ); - } -}; - -template <> -bool Writer<ppc>::addBranchIslands() -{ - return this->createBranchIslands(); -} - -template <> -bool Writer<ppc64>::addBranchIslands() -{ - return this->createBranchIslands(); -} - -template <> -bool Writer<x86>::addBranchIslands() -{ - // x86 branches can reach entire 4G address space, so no need for branch islands - return false; -} - -template <> -bool Writer<x86_64>::addBranchIslands() -{ - // x86 branches can reach entire 4G size of largest image - return false; -} - -template <> -bool Writer<arm>::addBranchIslands() -{ - return this->createBranchIslands(); -} - -template <> -bool Writer<ppc>::isBranchThatMightNeedIsland(uint8_t kind) -{ - switch (kind) { - case ppc::kBranch24: - case ppc::kBranch24WeakImport: - return true; - } - return false; -} - -template <> -bool Writer<ppc64>::isBranchThatMightNeedIsland(uint8_t kind) -{ - switch (kind) { - case ppc64::kBranch24: - case ppc64::kBranch24WeakImport: - return true; - } - return false; -} - -template <> -bool Writer<arm>::isBranchThatMightNeedIsland(uint8_t kind) -{ - switch (kind) { - case arm::kBranch24: - case arm::kBranch24WeakImport: - case arm::kThumbBranch22: - case arm::kThumbBranch22WeakImport: - return true; - } - return false; -} - -template <> -uint32_t Writer<ppc>::textSizeWhenMightNeedBranchIslands() -{ - return 16000000; -} - -template <> -uint32_t Writer<ppc64>::textSizeWhenMightNeedBranchIslands() -{ - return 16000000; -} - -template <> -uint32_t Writer<arm>::textSizeWhenMightNeedBranchIslands() -{ - if ( fHasThumbBranches == false ) - return 32000000; // ARM can branch +/- 32MB - else if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) - return 16000000; // thumb2 can branch +/- 16MB - else - return 4000000; // thumb1 can branch +/- 4MB -} - -template <> -uint32_t Writer<ppc>::maxDistanceBetweenIslands() -{ - return 14*1024*1024; -} - -template <> -uint32_t Writer<ppc64>::maxDistanceBetweenIslands() -{ - return 14*1024*1024; -} - -template <> -uint32_t Writer<arm>::maxDistanceBetweenIslands() -{ - if ( fHasThumbBranches == false ) - return 30*1024*1024; - else if ( fOptions.preferSubArchitecture() && fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) - return 14*1024*1024; - else - return 3500000; -} - - -// -// PowerPC can do PC relative branches as far as +/-16MB. -// If a branch target is >16MB then we insert one or more -// "branch islands" between the branch and its target that -// allows island hopping to the target. -// -// Branch Island Algorithm -// -// If the __TEXT segment < 16MB, then no branch islands needed -// Otherwise, every 14MB into the __TEXT segment a region is -// added which can contain branch islands. Every out-of-range -// bl instruction is checked. If it crosses a region, an island -// is added to that region with the same target and the bl is -// adjusted to target the island instead. -// -// In theory, if too many islands are added to one region, it -// could grow the __TEXT enough that other previously in-range -// bl branches could be pushed out of range. We reduce the -// probability this could happen by placing the ranges every -// 14MB which means the region would have to be 2MB (512,000 islands) -// before any branches could be pushed out of range. -// -template <typename A> -bool Writer<A>::createBranchIslands() -{ - bool log = false; - bool result = false; - // Can only possibly need branch islands if __TEXT segment > 16M - if ( fLoadCommandsSegment->fSize > textSizeWhenMightNeedBranchIslands() ) { - if ( log) fprintf(stderr, "ld: checking for branch islands, __TEXT segment size=%llu\n", fLoadCommandsSegment->fSize); - const uint32_t kBetweenRegions = maxDistanceBetweenIslands(); // place regions of islands every 14MB in __text section - SectionInfo* textSection = NULL; - for (std::vector<SectionInfo*>::iterator it=fLoadCommandsSegment->fSections.begin(); it != fLoadCommandsSegment->fSections.end(); it++) { - if ( strcmp((*it)->fSectionName, "__text") == 0 ) { - textSection = *it; - if ( log) fprintf(stderr, "ld: checking for branch islands, __text section size=%llu\n", textSection->fSize); - break; - } - } - const int kIslandRegionsCount = fLoadCommandsSegment->fSize / kBetweenRegions; - typedef std::map<TargetAndOffset,ObjectFile::Atom*, TargetAndOffsetComparor> AtomToIsland; - AtomToIsland regionsMap[kIslandRegionsCount]; - std::vector<ObjectFile::Atom*> regionsIslands[kIslandRegionsCount]; - unsigned int islandCount = 0; - if (log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); - - // create islands for branch references that are out of range - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms->begin(); it != fAllAtoms->end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector<ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( this->isBranchThatMightNeedIsland(ref->getKind()) ) { - ObjectFile::Atom& target = ref->getTarget(); - int64_t srcAddr = atom->getAddress() + ref->getFixUpOffset(); - int64_t dstAddr = target.getAddress() + ref->getTargetOffset(); - int64_t displacement = dstAddr - srcAddr; - TargetAndOffset finalTargetAndOffset = { &target, ref->getTargetOffset() }; - const int64_t kBranchLimit = kBetweenRegions; - if ( displacement > kBranchLimit ) { - // create forward branch chain - ObjectFile::Atom* nextTarget = ⌖ - for (int i=kIslandRegionsCount-1; i >=0 ; --i) { - AtomToIsland* region = ®ionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1) + textSection->getBaseAddress(); - if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { - AtomToIsland::iterator pos = region->find(finalTargetAndOffset); - if ( pos == region->end() ) { - BranchIslandAtom<A>* island = new BranchIslandAtom<A>(*this, target.getDisplayName(), i, *nextTarget, *finalTargetAndOffset.atom, finalTargetAndOffset.offset); - island->setSection(textSection); - (*region)[finalTargetAndOffset] = island; - if (log) fprintf(stderr, "added island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); - regionsIslands[i].push_back(island); - ++islandCount; - nextTarget = island; - } - else { - nextTarget = pos->second; - } - } - } - if (log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->getDisplayName(), target.getDisplayName(), atom->getDisplayName()); - ref->setTarget(*nextTarget, 0); - } - else if ( displacement < (-kBranchLimit) ) { - // create back branching chain - ObjectFile::Atom* prevTarget = ⌖ - for (int i=0; i < kIslandRegionsCount ; ++i) { - AtomToIsland* region = ®ionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { - AtomToIsland::iterator pos = region->find(finalTargetAndOffset); - if ( pos == region->end() ) { - BranchIslandAtom<A>* island = new BranchIslandAtom<A>(*this, target.getDisplayName(), i, *prevTarget, *finalTargetAndOffset.atom, finalTargetAndOffset.offset); - island->setSection(textSection); - (*region)[finalTargetAndOffset] = island; - if (log) fprintf(stderr, "added back island %s to region %d for %s\n", island->getDisplayName(), i, atom->getDisplayName()); - regionsIslands[i].push_back(island); - ++islandCount; - prevTarget = island; - } - else { - prevTarget = pos->second; - } - } - } - if (log) fprintf(stderr, "using back island %s for %s\n", prevTarget->getDisplayName(), atom->getDisplayName()); - ref->setTarget(*prevTarget, 0); - } - } - } - } - - // insert islands into __text section and adjust section offsets - if ( islandCount > 0 ) { - if ( log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); - std::vector<ObjectFile::Atom*> newAtomList; - newAtomList.reserve(textSection->fAtoms.size()+islandCount); - uint64_t islandRegionAddr = kBetweenRegions + textSection->getBaseAddress(); - uint64_t textSectionAlignment = (1 << textSection->fAlignment); - int regionIndex = 0; - uint64_t atomSlide = 0; - uint64_t sectionOffset = 0; - for (std::vector<ObjectFile::Atom*>::iterator it=textSection->fAtoms.begin(); it != textSection->fAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getAddress()+atom->getSize()) > islandRegionAddr ) { - uint64_t islandStartOffset = atom->getSectionOffset() + atomSlide; - sectionOffset = islandStartOffset; - std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex]; - for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - ObjectFile::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - islandAtom->setSectionOffset(sectionOffset); - if ( log ) fprintf(stderr, "assigning __text offset 0x%08llx to %s\n", sectionOffset, islandAtom->getDisplayName()); - sectionOffset += islandAtom->getSize(); - } - ++regionIndex; - islandRegionAddr += kBetweenRegions; - uint64_t islandRegionAlignmentBlocks = (sectionOffset - islandStartOffset + textSectionAlignment - 1) / textSectionAlignment; - atomSlide += (islandRegionAlignmentBlocks * textSectionAlignment); - } - newAtomList.push_back(atom); - if ( atomSlide != 0 ) - atom->setSectionOffset(atom->getSectionOffset()+atomSlide); - } - sectionOffset = textSection->fSize+atomSlide; - // put any remaining islands at end of __text section - if ( regionIndex < kIslandRegionsCount ) { - std::vector<ObjectFile::Atom*>* regionIslands = ®ionsIslands[regionIndex]; - for (std::vector<ObjectFile::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - ObjectFile::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - uint64_t alignment = 1 << (islandAtom->getAlignment().powerOf2); - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - islandAtom->setSectionOffset(sectionOffset); - if ( log ) fprintf(stderr, "assigning __text offset 0x%08llx to %s\n", sectionOffset, islandAtom->getDisplayName()); - sectionOffset += islandAtom->getSize(); - } - } - - textSection->fAtoms = newAtomList; - textSection->fSize = sectionOffset; - result = true; - } - - } - return result; -} - - -template <typename A> -void Writer<A>::adjustLoadCommandsAndPadding() -{ - fSegmentCommands->computeSize(); - - // recompute load command section offsets - uint64_t offset = 0; - std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fLoadCommandsSection->fAtoms; - const unsigned int atomCount = loadCommandAtoms.size(); - for (unsigned int i=0; i < atomCount; ++i) { - ObjectFile::Atom* atom = loadCommandAtoms[i]; - uint64_t alignment = 1 << atom->getAlignment().powerOf2; - offset = ( (offset+alignment-1) & (-alignment) ); - atom->setSectionOffset(offset); - uint32_t atomSize = atom->getSize(); - if ( atomSize > fLargestAtomSize ) - fLargestAtomSize = atomSize; - offset += atomSize; - fLoadCommandsSection->fSize = offset; - } - const uint32_t sizeOfLoadCommandsPlusHeader = offset + sizeof(macho_header<typename A::P>); - - std::vector<SectionInfo*>& sectionInfos = fLoadCommandsSegment->fSections; - const int sectionCount = sectionInfos.size(); - uint32_t totalSizeOfTEXTLessHeaderAndLoadCommands = 0; - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) - break; - totalSizeOfTEXTLessHeaderAndLoadCommands += curSection->fSize; - } - uint64_t paddingSize = 0; - if ( fOptions.outputKind() == Options::kDyld ) { - // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address - paddingSize = 4096 - (totalSizeOfTEXTLessHeaderAndLoadCommands % 4096); - } - else if ( fOptions.outputKind() == Options::kObjectFile ) { - // mach-o .o files need no padding between load commands and first section - // but leave enough room that the object file could be signed - paddingSize = 32; - } - else if ( fOptions.outputKind() == Options::kPreload ) { - // mach-o MH_PRELOAD files need no padding between load commands and first section - paddingSize = 0; - } - else { - // work backwards from end of segment and lay out sections so that extra room goes to padding atom - uint64_t addr = 0; - for(int j=sectionCount-1; j >=0; --j) { - SectionInfo* curSection = sectionInfos[j]; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) { - addr -= (fLoadCommandsSection->fSize+fMachHeaderAtom->getSize()); - paddingSize = addr % fOptions.segmentAlignment(); - break; - } - addr -= curSection->fSize; - addr = addr & (0 - (1 << curSection->fAlignment)); - } - - // if command line requires more padding than this - uint32_t minPad = fOptions.minimumHeaderPad(); - if ( fOptions.maxMminimumHeaderPad() ) { - // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes - uint32_t altMin = fLibraryToOrdinal.size() * MAXPATHLEN; - if ( fOptions.outputKind() == Options::kDynamicLibrary ) - altMin += MAXPATHLEN; - if ( altMin > minPad ) - minPad = altMin; - } - if ( paddingSize < minPad ) { - int extraPages = (minPad - paddingSize + fOptions.segmentAlignment() - 1)/fOptions.segmentAlignment(); - paddingSize += extraPages * fOptions.segmentAlignment(); - } - - if ( fOptions.makeEncryptable() ) { - // load commands must be on a separate non-encrypted page - int loadCommandsPage = (sizeOfLoadCommandsPlusHeader + minPad)/fOptions.segmentAlignment(); - int textPage = (sizeOfLoadCommandsPlusHeader + paddingSize)/fOptions.segmentAlignment(); - if ( loadCommandsPage == textPage ) { - paddingSize += fOptions.segmentAlignment(); - textPage += 1; - } - - //paddingSize = 4096 - ((totalSizeOfTEXTLessHeaderAndLoadCommands+fOptions.minimumHeaderPad()) % 4096) + fOptions.minimumHeaderPad(); - fEncryptionLoadCommand->setStartEncryptionOffset(textPage*fOptions.segmentAlignment()); - } - } - - // adjust atom size and update section size - fHeaderPadding->setSize(paddingSize); - for(int j=0; j < sectionCount; ++j) { - SectionInfo* curSection = sectionInfos[j]; - if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 ) - curSection->fSize = paddingSize; - } -} - -static uint64_t segmentAlign(uint64_t addr, uint64_t alignment) -{ - return ((addr+alignment-1) & (-alignment)); -} - -// assign file offsets and logical address to all segments -template <typename A> -void Writer<A>::assignFileOffsets() -{ - const bool virtualSectionOccupyAddressSpace = ((fOptions.outputKind() != Options::kObjectFile) - && (fOptions.outputKind() != Options::kPreload)); - bool haveFixedSegments = false; - uint64_t fileOffset = 0; - uint64_t nextContiguousAddress = fOptions.baseAddress(); - uint64_t nextReadOnlyAddress = fOptions.baseAddress(); - uint64_t nextWritableAddress = fOptions.baseWritableAddress(); - - // process segments with fixed addresses (-segaddr) - for (std::vector<Options::SegmentStart>::iterator it = fOptions.customSegmentAddresses().begin(); it != fOptions.customSegmentAddresses().end(); ++it) { - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, it->name) == 0 ) { - curSegment->fBaseAddress = it->address; - curSegment->fFixedAddress = true; - break; - } - } - } - - // process segments with fixed addresses (-seg_page_size) - for (std::vector<Options::SegmentSize>::iterator it = fOptions.customSegmentSizes().begin(); it != fOptions.customSegmentSizes().end(); ++it) { - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, it->name) == 0 ) { - curSegment->fPageSize = it->size; - break; - } - } - } - - // Run through the segments and each segment's sections to assign addresses - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - - if ( fOptions.splitSeg() ) { - if ( curSegment->fInitProtection & VM_PROT_WRITE ) - nextContiguousAddress = nextWritableAddress; - else - nextContiguousAddress = nextReadOnlyAddress; - } - - if ( fOptions.outputKind() == Options::kPreload ) { - if ( strcmp(curSegment->fName, "__HEADER") == 0 ) - nextContiguousAddress = 0; - else if ( strcmp(curSegment->fName, "__TEXT") == 0 ) - nextContiguousAddress = fOptions.baseAddress(); - } - - fileOffset = segmentAlign(fileOffset, curSegment->fPageSize); - curSegment->fFileOffset = fileOffset; - - // Set the segment base address - if ( curSegment->fFixedAddress ) - haveFixedSegments = true; - else - curSegment->fBaseAddress = segmentAlign(nextContiguousAddress, curSegment->fPageSize); - - // We've set the segment address, now run through each section. - uint64_t address = curSegment->fBaseAddress; - SectionInfo* firstZeroFillSection = NULL; - SectionInfo* prevSection = NULL; - - std::vector<SectionInfo*>& sectionInfos = curSegment->fSections; - - for (std::vector<SectionInfo*>::iterator it = sectionInfos.begin(); it != sectionInfos.end(); ++it) { - SectionInfo* curSection = *it; - - // adjust section address based on alignment - uint64_t alignment = 1 << curSection->fAlignment; - if ( curSection->fAtoms.size() == 1 ) { - // if there is only one atom in section, use modulus for even better layout - ObjectFile::Alignment atomAlign = curSection->fAtoms[0]->getAlignment(); - uint64_t atomAlignP2 = (1 << atomAlign.powerOf2); - uint64_t currentModulus = (address % atomAlignP2); - if ( currentModulus != atomAlign.modulus ) { - if ( atomAlign.modulus > currentModulus ) - address += atomAlign.modulus-currentModulus; - else - address += atomAlign.modulus+atomAlignP2-currentModulus; - } - } - else { - address = ( (address+alignment-1) & (-alignment) ); - } - // adjust file offset to match address - if ( prevSection != NULL ) { - if ( virtualSectionOccupyAddressSpace || !prevSection->fVirtualSection ) - fileOffset = (address - prevSection->getBaseAddress()) + prevSection->fFileOffset; - else - fileOffset = ( (fileOffset+alignment-1) & (-alignment) ); - } - - // update section info - curSection->fFileOffset = fileOffset; - curSection->setBaseAddress(address); - //fprintf(stderr, "%s %s addr=0x%llX, fileoffset=0x%llX, size=0x%llX\n", curSegment->fName, curSection->fSectionName, address, fileOffset, curSection->fSize); - - // keep track of trailing zero fill sections - if ( curSection->fAllZeroFill && (firstZeroFillSection == NULL) ) - firstZeroFillSection = curSection; - if ( !curSection->fAllZeroFill && (firstZeroFillSection != NULL) && (fOptions.outputKind() != Options::kObjectFile) ) - throwf("zero-fill section %s not at end of segment", curSection->fSectionName); - - // update running pointers - if ( virtualSectionOccupyAddressSpace || !curSection->fVirtualSection ) - address += curSection->fSize; - fileOffset += curSection->fSize; - - // sanity check size of 32-bit binaries - if ( address > maxAddress() ) - throwf("section %s exceeds 4GB limit", curSection->fSectionName); - - // update segment info - curSegment->fFileSize = fileOffset - curSegment->fFileOffset; - curSegment->fSize = curSegment->fFileSize; - prevSection = curSection; - } - - if ( fOptions.outputKind() == Options::kObjectFile ) { - // don't page align .o files - } - else { - // optimize trailing zero-fill sections to not occupy disk space - if ( firstZeroFillSection != NULL ) { - curSegment->fFileSize = firstZeroFillSection->fFileOffset - curSegment->fFileOffset; - fileOffset = firstZeroFillSection->fFileOffset; - } - // page align segment size - curSegment->fFileSize = segmentAlign(curSegment->fFileSize, curSegment->fPageSize); - curSegment->fSize = segmentAlign(curSegment->fSize, curSegment->fPageSize); - if ( !curSegment->fIndependentAddress && (curSegment->fBaseAddress >= nextContiguousAddress) ) { - nextContiguousAddress = segmentAlign(curSegment->fBaseAddress+curSegment->fSize, curSegment->fPageSize); - fileOffset = segmentAlign(fileOffset, curSegment->fPageSize); - if ( curSegment->fInitProtection & VM_PROT_WRITE ) - nextWritableAddress = nextContiguousAddress; - else - nextReadOnlyAddress = nextContiguousAddress; - } - } - //fprintf(stderr, "end of seg %s, fileoffset=0x%llX, nextContiguousAddress=0x%llX\n", curSegment->fName, fileOffset, nextContiguousAddress); - } - - // check for segment overlaps caused by user specified fixed segments (e.g. __PAGEZERO, __UNIXSTACK) - if ( haveFixedSegments ) { - int segCount = fSegmentInfos.size(); - for(int i=0; i < segCount; ++i) { - SegmentInfo* segment1 = fSegmentInfos[i]; - - for(int j=0; j < segCount; ++j) { - if ( i != j ) { - SegmentInfo* segment2 = fSegmentInfos[j]; - - if ( segment1->fBaseAddress < segment2->fBaseAddress ) { - if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress ) - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - else if ( segment1->fBaseAddress > segment2->fBaseAddress ) { - if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress ) - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - else if ( (segment1->fSize != 0) && (segment2->fSize != 0) ) { - throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", - segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize); - } - } - } - } - } - - // set up fFirstWritableSegment and fWritableSegmentPastFirst4GB - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( (curSegment->fInitProtection & VM_PROT_WRITE) != 0 ) { - if ( fFirstWritableSegment == NULL ) - fFirstWritableSegment = curSegment; - if ( (curSegment->fBaseAddress + curSegment->fSize - fOptions.baseAddress()) >= 0x100000000LL ) - fWritableSegmentPastFirst4GB = true; - } - } - - // record size of encrypted part of __TEXT segment - if ( fOptions.makeEncryptable() ) { - for (std::vector<SegmentInfo*>::iterator segit = fSegmentInfos.begin(); segit != fSegmentInfos.end(); ++segit) { - SegmentInfo* curSegment = *segit; - if ( strcmp(curSegment->fName, "__TEXT") == 0 ) { - fEncryptionLoadCommand->setEndEncryptionOffset(curSegment->fFileSize); - break; - } - } - } - -} - -template <typename A> -void Writer<A>::adjustLinkEditSections() -{ - // link edit content is always in last segment - SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1]; - unsigned int firstLinkEditSectionIndex = 0; - while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 ) - ++firstLinkEditSectionIndex; - - const unsigned int linkEditSectionCount = lastSeg->fSections.size(); - uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset; - uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress(); - if ( fPadSegmentInfo != NULL ) { - // insert __4GBFILL segment into segments vector before LINKEDIT - for(std::vector<SegmentInfo*>::iterator it = fSegmentInfos.begin(); it != fSegmentInfos.end(); ++it) { - if ( *it == lastSeg ) { - fSegmentInfos.insert(it, fPadSegmentInfo); - break; - } - } - // adjust __4GBFILL segment to span from end of last segment to zeroPageSize - fPadSegmentInfo->fSize = fOptions.zeroPageSize() - address; - fPadSegmentInfo->fBaseAddress = address; - // adjust LINKEDIT to start at zeroPageSize - address = fOptions.zeroPageSize(); - lastSeg->fBaseAddress = fOptions.zeroPageSize(); - } - for (unsigned int i=firstLinkEditSectionIndex; i < linkEditSectionCount; ++i) { - std::vector<class ObjectFile::Atom*>& atoms = lastSeg->fSections[i]->fAtoms; - // adjust section address based on alignment - uint64_t sectionAlignment = 1 << lastSeg->fSections[i]->fAlignment; - uint64_t pad = ((address+sectionAlignment-1) & (-sectionAlignment)) - address; - address += pad; - fileOffset += pad; // adjust file offset to match address - lastSeg->fSections[i]->setBaseAddress(address); - if ( strcmp(lastSeg->fSections[i]->fSectionName, "._absolute") == 0 ) - lastSeg->fSections[i]->setBaseAddress(0); - lastSeg->fSections[i]->fFileOffset = fileOffset; - uint64_t sectionOffset = 0; - for (unsigned int j=0; j < atoms.size(); ++j) { - ObjectFile::Atom* atom = atoms[j]; - uint64_t alignment = 1 << atom->getAlignment().powerOf2; - sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) ); - atom->setSectionOffset(sectionOffset); - uint64_t size = atom->getSize(); - sectionOffset += size; - if ( size > fLargestAtomSize ) - fLargestAtomSize = size; - } - //fprintf(stderr, "setting: lastSeg->fSections[%d]->fSize = 0x%08llX\n", i, sectionOffset); - lastSeg->fSections[i]->fSize = sectionOffset; - fileOffset += sectionOffset; - address += sectionOffset; - } - if ( fOptions.outputKind() == Options::kObjectFile ) { - //lastSeg->fBaseAddress = 0; - //lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]-> - //lastSeg->fFileOffset = 0; - //lastSeg->fFileSize = - } - else { - lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset; - lastSeg->fSize = (address - lastSeg->fBaseAddress+4095) & (-4096); - } -} - - -template <typename A> -ObjectFile::Atom::Scope MachHeaderAtom<A>::getScope() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - return ObjectFile::Atom::scopeGlobal; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return ObjectFile::Atom::scopeLinkageUnit; - } - throw "unknown header type"; -} - -template <typename A> -ObjectFile::Atom::SymbolTableInclusion MachHeaderAtom<A>::getSymbolTableInclusion() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - return ObjectFile::Atom::kSymbolTableInAndNeverStrip; - case Options::kStaticExecutable: - return ObjectFile::Atom::kSymbolTableInAsAbsolute; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - return ObjectFile::Atom::kSymbolTableIn; - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return ObjectFile::Atom::kSymbolTableNotIn; - } - throw "unknown header type"; -} - -template <typename A> -const char* MachHeaderAtom<A>::getName() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - return "__mh_execute_header"; - case Options::kDynamicLibrary: - return "__mh_dylib_header"; - case Options::kDynamicBundle: - return "__mh_bundle_header"; - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return NULL; - case Options::kDyld: - return "__mh_dylinker_header"; - } - throw "unknown header type"; -} - -template <typename A> -const char* MachHeaderAtom<A>::getDisplayName() const -{ - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - return this->getName(); - case Options::kObjectFile: - case Options::kPreload: - case Options::kKextBundle: - return "mach header"; - } - throw "unknown header type"; -} - -template <typename A> -void MachHeaderAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - // get file type - uint32_t fileType = 0; - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - fileType = MH_EXECUTE; - break; - case Options::kDynamicLibrary: - fileType = MH_DYLIB; - break; - case Options::kDynamicBundle: - fileType = MH_BUNDLE; - break; - case Options::kObjectFile: - fileType = MH_OBJECT; - break; - case Options::kDyld: - fileType = MH_DYLINKER; - break; - case Options::kPreload: - fileType = MH_PRELOAD; - break; - case Options::kKextBundle: - fileType = MH_KEXT_BUNDLE; - break; - } - - // get flags - uint32_t flags = 0; - if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) { - if ( fWriter.fCanScatter ) - flags = MH_SUBSECTIONS_VIA_SYMBOLS; - } - else { - if ( fWriter.fOptions.outputKind() == Options::kStaticExecutable ) { - flags |= MH_NOUNDEFS; - } - else if ( fWriter.fOptions.outputKind() == Options::kPreload ) { - flags |= MH_NOUNDEFS; - if ( fWriter.fOptions.positionIndependentExecutable() ) - flags |= MH_PIE; - } - else { - flags = MH_DYLDLINK; - if ( fWriter.fOptions.bindAtLoad() ) - flags |= MH_BINDATLOAD; - switch ( fWriter.fOptions.nameSpace() ) { - case Options::kTwoLevelNameSpace: - flags |= MH_TWOLEVEL | MH_NOUNDEFS; - break; - case Options::kFlatNameSpace: - break; - case Options::kForceFlatNameSpace: - flags |= MH_FORCE_FLAT; - break; - } - bool hasWeakDefines = fWriter.fHasWeakExports; - if ( fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->size() != 0 ) { - for(std::set<const ObjectFile::Atom*>::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin(); - it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) { - if ( fWriter.shouldExport(**it) ) { - hasWeakDefines = true; - break; - } - } - } - if ( hasWeakDefines ) - flags |= MH_WEAK_DEFINES; - if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports ) - flags |= MH_BINDS_TO_WEAK; - if ( fWriter.fOptions.prebind() ) - flags |= MH_PREBOUND; - if ( fWriter.fOptions.splitSeg() ) - flags |= MH_SPLIT_SEGS; - if ( (fWriter.fOptions.outputKind() == Options::kDynamicLibrary) && fWriter.fNoReExportedDylibs ) - flags |= MH_NO_REEXPORTED_DYLIBS; - if ( fWriter.fOptions.positionIndependentExecutable() ) - flags |= MH_PIE; - if ( fWriter.fOptions.markAutoDeadStripDylib() ) - flags |= MH_DEAD_STRIPPABLE_DYLIB; - } - if ( fWriter.fOptions.hasExecutableStack() ) - flags |= MH_ALLOW_STACK_EXECUTION; - if ( fWriter.fOptions.readerOptions().fRootSafe ) - flags |= MH_ROOT_SAFE; - if ( fWriter.fOptions.readerOptions().fSetuidSafe ) - flags |= MH_SETUID_SAFE; - } - - // get commands info - uint32_t commandsSize = 0; - uint32_t commandsCount = 0; - - std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms; - for (std::vector<ObjectFile::Atom*>::iterator it=loadCommandAtoms.begin(); it != loadCommandAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - commandsSize += atom->getSize(); - // segment and symbol table atoms can contain more than one load command - if ( atom == fWriter.fSegmentCommands ) - commandsCount += fWriter.fSegmentCommands->commandCount(); - else if ( atom == fWriter.fSymbolTableCommands ) - commandsCount += fWriter.fSymbolTableCommands->commandCount(); - else if ( atom->getSize() != 0 ) - ++commandsCount; - } - - // fill out mach_header - macho_header<typename A::P>* mh = (macho_header<typename A::P>*)buffer; - setHeaderInfo(*mh); - mh->set_filetype(fileType); - mh->set_ncmds(commandsCount); - mh->set_sizeofcmds(commandsSize); - mh->set_flags(flags); -} - -template <> -void MachHeaderAtom<ppc>::setHeaderInfo(macho_header<ppc::P>& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_POWERPC); - header.set_cpusubtype(fWriter.fCpuConstraint); -} - -template <> -void MachHeaderAtom<ppc64>::setHeaderInfo(macho_header<ppc64::P>& header) const -{ - header.set_magic(MH_MAGIC_64); - header.set_cputype(CPU_TYPE_POWERPC64); - if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL | 0x80000000); - else - header.set_cpusubtype(CPU_SUBTYPE_POWERPC_ALL); - header.set_reserved(0); -} - -template <> -void MachHeaderAtom<x86>::setHeaderInfo(macho_header<x86::P>& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_I386); - header.set_cpusubtype(CPU_SUBTYPE_I386_ALL); -} - -template <> -void MachHeaderAtom<x86_64>::setHeaderInfo(macho_header<x86_64::P>& header) const -{ - header.set_magic(MH_MAGIC_64); - header.set_cputype(CPU_TYPE_X86_64); - if ( (fWriter.fOptions.outputKind() == Options::kDynamicExecutable) && (fWriter.fOptions.macosxVersionMin() >= ObjectFile::ReaderOptions::k10_5) ) - header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL | 0x80000000); - else - header.set_cpusubtype(CPU_SUBTYPE_X86_64_ALL); - header.set_reserved(0); -} - -template <> -void MachHeaderAtom<arm>::setHeaderInfo(macho_header<arm::P>& header) const -{ - header.set_magic(MH_MAGIC); - header.set_cputype(CPU_TYPE_ARM); - header.set_cpusubtype(fWriter.fCpuConstraint); -} - -template <typename A> -CustomStackAtom<A>::CustomStackAtom(Writer<A>& writer) - : WriterAtom<A>(writer, Segment::fgStackSegment) -{ - if ( stackGrowsDown() ) - Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize()); - else - Segment::fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr()); -} - - -template <> bool CustomStackAtom<ppc>::stackGrowsDown() { return true; } -template <> bool CustomStackAtom<ppc64>::stackGrowsDown() { return true; } -template <> bool CustomStackAtom<x86>::stackGrowsDown() { return true; } -template <> bool CustomStackAtom<x86_64>::stackGrowsDown() { return true; } -template <> bool CustomStackAtom<arm>::stackGrowsDown() { return true; } - -template <typename A> -void SegmentLoadCommandsAtom<A>::computeSize() -{ - uint64_t size = 0; - std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos; - int segCount = 0; - for(std::vector<SegmentInfo*>::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) { - SegmentInfo* seg = *it; - if ( seg->fHasLoadCommand ) { - ++segCount; - size += sizeof(macho_segment_command<P>); - std::vector<SectionInfo*>& sectionInfos = seg->fSections; - const int sectionCount = sectionInfos.size(); - for(int j=0; j < sectionCount; ++j) { - if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection ) - size += sizeof(macho_section<P>); - } - } - } - fSize = size; - fCommandCount = segCount; - if ( fWriter.fPadSegmentInfo != NULL ) { - ++fCommandCount; - fSize += sizeof(macho_segment_command<P>); - } -} - -template <> -uint64_t LoadCommandAtom<ppc>::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template <> -uint64_t LoadCommandAtom<ppc64>::alignedSize(uint64_t size) -{ - return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o -} - -template <> -uint64_t LoadCommandAtom<x86>::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template <> -uint64_t LoadCommandAtom<x86_64>::alignedSize(uint64_t size) -{ - return ((size+7) & (-8)); // 8-byte align all load commands for 64-bit mach-o -} - -template <> -uint64_t LoadCommandAtom<arm>::alignedSize(uint64_t size) -{ - return ((size+3) & (-4)); // 4-byte align all load commands for 32-bit mach-o -} - -template <typename A> -void SegmentLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile ); - bzero(buffer, size); - uint8_t* p = buffer; - typename std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos; - for(std::vector<SegmentInfo*>::iterator it = segmentInfos.begin(); it != segmentInfos.end(); ++it) { - SegmentInfo* segInfo = *it; - if ( ! segInfo->fHasLoadCommand ) - continue; - const int sectionCount = segInfo->fSections.size(); - macho_segment_command<P>* cmd = (macho_segment_command<P>*)p; - cmd->set_cmd(macho_segment_command<P>::CMD); - cmd->set_segname(segInfo->fName); - cmd->set_vmaddr(segInfo->fBaseAddress); - cmd->set_vmsize(oneSegment ? 0 : segInfo->fSize); - cmd->set_fileoff(segInfo->fFileOffset); - cmd->set_filesize(oneSegment ? 0 : segInfo->fFileSize); - cmd->set_maxprot(segInfo->fMaxProtection); - cmd->set_initprot(segInfo->fInitProtection); - // add sections array - macho_section<P>* const sections = (macho_section<P>*)&p[sizeof(macho_segment_command<P>)]; - unsigned int sectionsEmitted = 0; - for (int j=0; j < sectionCount; ++j) { - SectionInfo* sectInfo = segInfo->fSections[j]; - if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) { - macho_section<P>* sect = §ions[sectionsEmitted++]; - if ( oneSegment ) { - // .o file segment does not cover load commands, so recalc at first real section - if ( sectionsEmitted == 1 ) { - cmd->set_vmaddr(sectInfo->getBaseAddress()); - cmd->set_fileoff(sectInfo->fFileOffset); - } - // <rdar://problem/7712869> if last section is zero-fill don't add size to filesize total - if ( !sectInfo->fAllZeroFill ) { - cmd->set_filesize((sectInfo->fFileOffset+sectInfo->fSize)-cmd->fileoff()); - } - cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize); - } - sect->set_sectname(sectInfo->fSectionName); - sect->set_segname(sectInfo->fSegmentName); - sect->set_addr(sectInfo->getBaseAddress()); - sect->set_size(sectInfo->fSize); - sect->set_offset(sectInfo->fFileOffset); - sect->set_align(sectInfo->fAlignment); - if ( sectInfo->fRelocCount != 0 ) { - sect->set_reloff(sectInfo->fRelocOffset * sizeof(macho_relocation_info<P>) + fWriter.fSectionRelocationsAtom->getFileOffset()); - sect->set_nreloc(sectInfo->fRelocCount); - } - if ( sectInfo->fAllZeroFill ) { - sect->set_flags(S_ZEROFILL); - sect->set_offset(0); - } - else if ( sectInfo->fAllLazyPointers ) { - sect->set_flags(S_LAZY_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllLazyDylibPointers ) { - sect->set_flags(S_LAZY_DYLIB_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllNonLazyPointers ) { - sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - } - else if ( sectInfo->fAllStubs ) { - sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); - if ( sectInfo->fHasTextLocalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); - } - else if ( sectInfo->fAllSelfModifyingStubs ) { - sect->set_flags(S_SYMBOL_STUBS | S_ATTR_SELF_MODIFYING_CODE); - sect->set_reserved1(sectInfo->fIndirectSymbolOffset); - sect->set_reserved2(sectInfo->fSize / sectInfo->fAtoms.size()); - } - else if ( sectInfo->fAllStubHelpers ) { - sect->set_flags(S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - if ( sectInfo->fHasTextLocalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); - } - else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCStringType ) { - sect->set_flags(S_CSTRING_LITERALS); - } - else if ( sectInfo->fAtoms.at(0)->getContentType() == ObjectFile::Atom::kCFIType ) { - sect->set_flags(S_COALESCED | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS); - } - else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_MOD_INIT_FUNC_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_MOD_TERM_FUNC_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__textcoal_nt") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_COALESCED); - } - else if ( (strcmp(sectInfo->fSectionName, "__const_coal") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_COALESCED); - } - else if ( (strcmp(sectInfo->fSectionName, "__interpose") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_INTERPOSING); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal4") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_4BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal8") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_8BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__literal16") == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_16BYTE_LITERALS); - } - else if ( (strcmp(sectInfo->fSectionName, "__message_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__objc_selrefs") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strcmp(sectInfo->fSectionName, "__cls_refs") == 0) && (strcmp(sectInfo->fSegmentName, "__OBJC") == 0) ) { - sect->set_flags(S_LITERAL_POINTERS); - } - else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_DTRACE_DOF); - } - else if ( (strncmp(sectInfo->fSectionName, "__dof_", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) { - sect->set_flags(S_DTRACE_DOF); - } - else if ( (strncmp(sectInfo->fSectionName, "__text", 6) == 0) && (strcmp(sectInfo->fSegmentName, "__TEXT") == 0) ) { - sect->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); - if ( sectInfo->fHasTextLocalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_LOC_RELOC); - if ( sectInfo->fHasTextExternalRelocs ) - sect->set_flags(sect->flags() | S_ATTR_EXT_RELOC); - } - //fprintf(stderr, "section %s flags=0x%08X\n", sectInfo->fSectionName, sect->flags()); - } - } - p = &p[sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>)]; - cmd->set_cmdsize(sizeof(macho_segment_command<P>) + sectionsEmitted*sizeof(macho_section<P>)); - cmd->set_nsects(sectionsEmitted); - } -} - - -template <typename A> -SymbolTableLoadCommandsAtom<A>::SymbolTableLoadCommandsAtom(Writer<A>& writer) - : LoadCommandAtom<A>(writer), fNeedsDynamicSymbolTable(false) -{ - bzero(&fSymbolTable, sizeof(macho_symtab_command<P>)); - bzero(&fDynamicSymbolTable, sizeof(macho_dysymtab_command<P>)); - switch ( fWriter.fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - case Options::kDyld: - case Options::kKextBundle: - fNeedsDynamicSymbolTable = true; - break; - case Options::kObjectFile: - case Options::kStaticExecutable: - fNeedsDynamicSymbolTable = false; - case Options::kPreload: - fNeedsDynamicSymbolTable = fWriter.fOptions.positionIndependentExecutable(); - break; - } - writer.fSymbolTableCommands = this; -} - - - -template <typename A> -void SymbolTableLoadCommandsAtom<A>::needDynamicTable() -{ - fNeedsDynamicSymbolTable = true; -} - - -template <typename A> -uint64_t SymbolTableLoadCommandsAtom<A>::getSize() const -{ - if ( fNeedsDynamicSymbolTable ) - return this->alignedSize(sizeof(macho_symtab_command<P>) + sizeof(macho_dysymtab_command<P>)); - else - return this->alignedSize(sizeof(macho_symtab_command<P>)); -} - -template <typename A> -void SymbolTableLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - // build LC_SYMTAB command - macho_symtab_command<P>* symbolTableCmd = (macho_symtab_command<P>*)buffer; - bzero(symbolTableCmd, sizeof(macho_symtab_command<P>)); - symbolTableCmd->set_cmd(LC_SYMTAB); - symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command<P>)); - symbolTableCmd->set_nsyms(fWriter.fSymbolTableCount); - symbolTableCmd->set_symoff(fWriter.fSymbolTableCount == 0 ? 0 : fWriter.fSymbolTableAtom->getFileOffset()); - symbolTableCmd->set_stroff(fWriter.fStringsAtom->getSize() == 0 ? 0 : fWriter.fStringsAtom->getFileOffset()); - symbolTableCmd->set_strsize(fWriter.fStringsAtom->getSize()); - - // build LC_DYSYMTAB command - if ( fNeedsDynamicSymbolTable ) { - macho_dysymtab_command<P>* dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)&buffer[sizeof(macho_symtab_command<P>)]; - bzero(dynamicSymbolTableCmd, sizeof(macho_dysymtab_command<P>)); - dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); - dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command<P>)); - dynamicSymbolTableCmd->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); - dynamicSymbolTableCmd->set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount); - dynamicSymbolTableCmd->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); - dynamicSymbolTableCmd->set_nextdefsym(fWriter.fSymbolTableExportCount); - dynamicSymbolTableCmd->set_iundefsym(fWriter.fSymbolTableImportStartIndex); - dynamicSymbolTableCmd->set_nundefsym(fWriter.fSymbolTableImportCount); - if ( fWriter.fModuleInfoAtom != NULL ) { - dynamicSymbolTableCmd->set_tocoff(fWriter.fModuleInfoAtom->getTableOfContentsFileOffset()); - dynamicSymbolTableCmd->set_ntoc(fWriter.fSymbolTableExportCount); - dynamicSymbolTableCmd->set_modtaboff(fWriter.fModuleInfoAtom->getModuleTableFileOffset()); - dynamicSymbolTableCmd->set_nmodtab(1); - dynamicSymbolTableCmd->set_extrefsymoff(fWriter.fModuleInfoAtom->getReferencesFileOffset()); - dynamicSymbolTableCmd->set_nextrefsyms(fWriter.fModuleInfoAtom->getReferencesCount()); - } - dynamicSymbolTableCmd->set_indirectsymoff((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nindirectsyms((fWriter.fIndirectTableAtom == NULL) ? 0 : fWriter.fIndirectTableAtom->fTable.size()); - if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) { - if ( fWriter.fExternalRelocationsAtom != 0 ) { - dynamicSymbolTableCmd->set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nextrel(fWriter.fExternalRelocs.size()); - } - if ( fWriter.fLocalRelocationsAtom != 0 ) { - dynamicSymbolTableCmd->set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset()); - dynamicSymbolTableCmd->set_nlocrel(fWriter.fInternalRelocs.size()); - } - } - } -} - - -template <typename A> -unsigned int SymbolTableLoadCommandsAtom<A>::commandCount() -{ - return fNeedsDynamicSymbolTable ? 2 : 1; -} - -template <typename A> -uint64_t DyldLoadCommandsAtom<A>::getSize() const -{ - return this->alignedSize(sizeof(macho_dylinker_command<P>) + strlen("/usr/lib/dyld") + 1); -} - -template <typename A> -void DyldLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_dylinker_command<P>* cmd = (macho_dylinker_command<P>*)buffer; - if ( fWriter.fOptions.outputKind() == Options::kDyld ) - cmd->set_cmd(LC_ID_DYLINKER); - else - cmd->set_cmd(LC_LOAD_DYLINKER); - cmd->set_cmdsize(this->getSize()); - cmd->set_name_offset(); - strcpy((char*)&buffer[sizeof(macho_dylinker_command<P>)], "/usr/lib/dyld"); -} - -template <typename A> -uint64_t AllowableClientLoadCommandsAtom<A>::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_client_command<P>) + strlen(this->clientString) + 1); -} - -template <typename A> -void AllowableClientLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - - bzero(buffer, size); - macho_sub_client_command<P>* cmd = (macho_sub_client_command<P>*)buffer; - cmd->set_cmd(LC_SUB_CLIENT); - cmd->set_cmdsize(size); - cmd->set_client_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_client_command<P>)], this->clientString); - -} - -template <typename A> -uint64_t DylibLoadCommandsAtom<A>::getSize() const -{ - if ( fOptimizedAway ) { - return 0; - } - else { - const char* path = fInfo.reader->getInstallPath(); - return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(path) + 1); - } -} - -template <typename A> -void DylibLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - if ( fOptimizedAway ) - return; - uint64_t size = this->getSize(); - bzero(buffer, size); - const char* path = fInfo.reader->getInstallPath(); - macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer; - // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB - bool autoWeakLoadDylib = ( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0) - && (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) ); - if ( fInfo.options.fLazyLoad ) - cmd->set_cmd(LC_LAZY_LOAD_DYLIB); - else if ( fInfo.options.fWeakImport || autoWeakLoadDylib ) - cmd->set_cmd(LC_LOAD_WEAK_DYLIB); - else if ( fInfo.options.fReExport && fWriter.fOptions.useSimplifiedDylibReExports() ) - cmd->set_cmd(LC_REEXPORT_DYLIB); - else - cmd->set_cmd(LC_LOAD_DYLIB); - cmd->set_cmdsize(this->getSize()); - cmd->set_timestamp(2); // needs to be some constant value that is different than DylibIDLoadCommandsAtom uses - cmd->set_current_version(fInfo.reader->getCurrentVersion()); - cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion()); - cmd->set_name_offset(); - strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], path); -} - - - -template <typename A> -uint64_t DylibIDLoadCommandsAtom<A>::getSize() const -{ - return this->alignedSize(sizeof(macho_dylib_command<P>) + strlen(fWriter.fOptions.installPath()) + 1); -} - -template <typename A> -void DylibIDLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)buffer; - cmd->set_cmd(LC_ID_DYLIB); - cmd->set_cmdsize(this->getSize()); - cmd->set_name_offset(); - cmd->set_timestamp(1); // needs to be some constant value that is different than DylibLoadCommandsAtom uses - cmd->set_current_version(fWriter.fOptions.currentVersion()); - cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion()); - strcpy((char*)&buffer[sizeof(macho_dylib_command<P>)], fWriter.fOptions.installPath()); -} - - -template <typename A> -void RoutinesLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - if (fWriter.fEntryPoint->isThumb()) - initAddr |= 1ULL; - bzero(buffer, sizeof(macho_routines_command<P>)); - macho_routines_command<P>* cmd = (macho_routines_command<P>*)buffer; - cmd->set_cmd(macho_routines_command<P>::CMD); - cmd->set_cmdsize(this->getSize()); - cmd->set_init_address(initAddr); -} - - -template <typename A> -uint64_t SubUmbrellaLoadCommandsAtom<A>::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_umbrella_command<P>) + strlen(fName) + 1); -} - -template <typename A> -void SubUmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_umbrella_command<P>* cmd = (macho_sub_umbrella_command<P>*)buffer; - cmd->set_cmd(LC_SUB_UMBRELLA); - cmd->set_cmdsize(this->getSize()); - cmd->set_sub_umbrella_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_umbrella_command<P>)], fName); -} - -template <typename A> -void UUIDLoadCommandAtom<A>::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 <typename A> -void UUIDLoadCommandAtom<A>::setContent(const uint8_t uuid[16]) -{ - memcpy(fUUID, uuid, 16); -} - -template <typename A> -void UUIDLoadCommandAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - if (fEmit) { - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_uuid_command<P>* cmd = (macho_uuid_command<P>*)buffer; - cmd->set_cmd(LC_UUID); - cmd->set_cmdsize(this->getSize()); - cmd->set_uuid((uint8_t*)fUUID); - } -} - - -template <typename A> -uint64_t SubLibraryLoadCommandsAtom<A>::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_library_command<P>) + fNameLength + 1); -} - -template <typename A> -void SubLibraryLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_library_command<P>* cmd = (macho_sub_library_command<P>*)buffer; - cmd->set_cmd(LC_SUB_LIBRARY); - cmd->set_cmdsize(this->getSize()); - cmd->set_sub_library_offset(); - strncpy((char*)&buffer[sizeof(macho_sub_library_command<P>)], fNameStart, fNameLength); - buffer[sizeof(macho_sub_library_command<P>)+fNameLength] = '\0'; -} - -template <typename A> -uint64_t UmbrellaLoadCommandsAtom<A>::getSize() const -{ - return this->alignedSize(sizeof(macho_sub_framework_command<P>) + strlen(fName) + 1); -} - -template <typename A> -void UmbrellaLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_sub_framework_command<P>* cmd = (macho_sub_framework_command<P>*)buffer; - cmd->set_cmd(LC_SUB_FRAMEWORK); - cmd->set_cmdsize(this->getSize()); - cmd->set_umbrella_offset(); - strcpy((char*)&buffer[sizeof(macho_sub_framework_command<P>)], fName); -} - -template <> -uint64_t ThreadsLoadCommandsAtom<ppc>::getSize() const -{ - return this->alignedSize(16 + 40*4); // base size + PPC_THREAD_STATE_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom<ppc64>::getSize() const -{ - return this->alignedSize(16 + 76*4); // base size + PPC_THREAD_STATE64_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom<x86>::getSize() const -{ - return this->alignedSize(16 + 16*4); // base size + i386_THREAD_STATE_COUNT * 4 -} - -template <> -uint64_t ThreadsLoadCommandsAtom<x86_64>::getSize() const -{ - return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); -} - -// We should be picking it up from a header -template <> -uint64_t ThreadsLoadCommandsAtom<arm>::getSize() const -{ - return this->alignedSize(16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4 -} - -template <> -void ThreadsLoadCommandsAtom<ppc>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command<ppc::P>* cmd = (macho_thread_command<ppc::P>*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); // PPC_THREAD_STATE - cmd->set_count(40); // PPC_THREAD_STATE_COUNT; - cmd->set_thread_register(0, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 -} - - -template <> -void ThreadsLoadCommandsAtom<ppc64>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command<ppc64::P>* cmd = (macho_thread_command<ppc64::P>*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(5); // PPC_THREAD_STATE64 - cmd->set_count(76); // PPC_THREAD_STATE64_COUNT; - cmd->set_thread_register(0, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(3, fWriter.fOptions.customStackAddr()); // r1 -} - -template <> -void ThreadsLoadCommandsAtom<x86>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command<x86::P>* cmd = (macho_thread_command<x86::P>*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); // i386_THREAD_STATE - cmd->set_count(16); // i386_THREAD_STATE_COUNT; - cmd->set_thread_register(10, start); - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // esp -} - -template <> -void ThreadsLoadCommandsAtom<x86_64>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - bzero(buffer, size); - macho_thread_command<x86_64::P>* cmd = (macho_thread_command<x86_64::P>*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(x86_THREAD_STATE64); - cmd->set_count(x86_THREAD_STATE64_COUNT); - cmd->set_thread_register(16, start); // rip - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(7, fWriter.fOptions.customStackAddr()); // uesp -} - -template <> -void ThreadsLoadCommandsAtom<arm>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint); - if ( fWriter.fEntryPoint->isThumb() ) - start |= 1ULL; - bzero(buffer, size); - macho_thread_command<arm::P>* cmd = (macho_thread_command<arm::P>*)buffer; - cmd->set_cmd(LC_UNIXTHREAD); - cmd->set_cmdsize(size); - cmd->set_flavor(1); - cmd->set_count(17); - cmd->set_thread_register(15, start); // pc - if ( fWriter.fOptions.hasCustomStack() ) - cmd->set_thread_register(13, fWriter.fOptions.customStackAddr()); // FIXME: sp? -} - -template <typename A> -uint64_t RPathLoadCommandsAtom<A>::getSize() const -{ - return this->alignedSize(sizeof(macho_rpath_command<P>) + strlen(fPath) + 1); -} - -template <typename A> -void RPathLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_rpath_command<P>* cmd = (macho_rpath_command<P>*)buffer; - cmd->set_cmd(LC_RPATH); - cmd->set_cmdsize(this->getSize()); - cmd->set_path_offset(); - strcpy((char*)&buffer[sizeof(macho_rpath_command<P>)], fPath); -} - - - -template <typename A> -void EncryptionLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - macho_encryption_info_command<P>* cmd = (macho_encryption_info_command<P>*)buffer; - cmd->set_cmd(LC_ENCRYPTION_INFO); - cmd->set_cmdsize(this->getSize()); - cmd->set_cryptoff(fStartOffset); - cmd->set_cryptsize(fEndOffset-fStartOffset); - cmd->set_cryptid(0); -} - - - -template <typename A> -void LoadCommandsPaddingAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - bzero(buffer, fSize); -} - -template <typename A> -void LoadCommandsPaddingAtom<A>::setSize(uint64_t newSize) -{ - fSize = newSize; - // this resizing by-passes the way fLargestAtomSize is set, so re-check here - if ( fWriter.fLargestAtomSize < newSize ) - fWriter.fLargestAtomSize = newSize; -} - -template <typename A> -void UnwindInfoAtom<A>::addUnwindInfo(ObjectFile::Atom* func, uint32_t offset, uint32_t encoding, - ObjectFile::Reference* fdeRef, ObjectFile::Reference* lsdaRef, - ObjectFile::Atom* personalityPointer) -{ - Info info; - info.func = func; - if ( fdeRef != NULL ) - info.fde = &fdeRef->getTarget(); - else - info.fde = NULL; - if ( lsdaRef != NULL ) { - info.lsda = &lsdaRef->getTarget(); - info.lsdaOffset = lsdaRef->getTargetOffset(); - } - else { - info.lsda = NULL; - info.lsdaOffset = 0; - } - info.personalityPointer = personalityPointer; - info.encoding = encoding; - fInfos.push_back(info); - //fprintf(stderr, "addUnwindInfo() encoding=0x%08X, lsda=%p, lsdaOffset=%d, person=%p, func=%s\n", - // encoding, info.lsda, info.lsdaOffset, personalityPointer, func->getDisplayName()); -} - -template <> -bool UnwindInfoAtom<x86>::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return ( (encoding & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); -} - -template <> -bool UnwindInfoAtom<x86_64>::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return ( (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); -} - -template <typename A> -bool UnwindInfoAtom<A>::encodingMeansUseDwarf(compact_unwind_encoding_t encoding) -{ - return false; -} - - -template <typename A> -void UnwindInfoAtom<A>::compressDuplicates(std::vector<Info>& uniqueInfos) -{ - // build new list removing entries where next function has same encoding - uniqueInfos.reserve(fInfos.size()); - Info last; - last.func = NULL; - last.lsda = NULL; - last.lsdaOffset = 0; - last.personalityPointer = NULL; - last.encoding = 0xFFFFFFFF; - for(typename std::vector<Info>::iterator it=fInfos.begin(); it != fInfos.end(); ++it) { - Info& newInfo = *it; - bool newNeedsDwarf = encodingMeansUseDwarf(newInfo.encoding); - // remove infos which have same encoding and personalityPointer as last one - if ( newNeedsDwarf || (newInfo.encoding != last.encoding) || (newInfo.personalityPointer != last.personalityPointer) - || (newInfo.lsda != NULL) || (last.lsda != NULL) ) { - uniqueInfos.push_back(newInfo); - } - last = newInfo; - } - //fprintf(stderr, "compressDuplicates() fInfos.size()=%lu, uniqueInfos.size()=%lu\n", fInfos.size(), uniqueInfos.size()); -} - -template <typename A> -void UnwindInfoAtom<A>::findCommonEncoding(const std::vector<Info>& uniqueInfos, std::map<uint32_t, unsigned int>& commonEncodings) -{ - // scan infos to get frequency counts for each encoding - std::map<uint32_t, unsigned int> encodingsUsed; - unsigned int mostCommonEncodingUsageCount = 0; - for(typename std::vector<Info>::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { - // never put dwarf into common table - if ( encodingMeansUseDwarf(it->encoding) ) - continue; - std::map<uint32_t, unsigned int>::iterator pos = encodingsUsed.find(it->encoding); - if ( pos == encodingsUsed.end() ) { - encodingsUsed[it->encoding] = 1; - } - else { - encodingsUsed[it->encoding] += 1; - if ( mostCommonEncodingUsageCount < encodingsUsed[it->encoding] ) - mostCommonEncodingUsageCount = encodingsUsed[it->encoding]; - } - } - // put the most common encodings into the common table, but at most 127 of them - for(unsigned int usages=mostCommonEncodingUsageCount; usages > 1; --usages) { - for (std::map<uint32_t, unsigned int>::iterator euit=encodingsUsed.begin(); euit != encodingsUsed.end(); ++euit) { - if ( euit->second == usages ) { - unsigned int size = commonEncodings.size(); - if ( size < 127 ) { - commonEncodings[euit->first] = size; - } - } - } - } -} - -template <typename A> -void UnwindInfoAtom<A>::makeLsdaIndex(const std::vector<Info>& uniqueInfos, std::map<ObjectFile::Atom*, uint32_t>& lsdaIndexOffsetMap) -{ - for(typename std::vector<Info>::const_iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { - lsdaIndexOffsetMap[it->func] = fLSDAIndex.size() * sizeof(macho_unwind_info_section_header_lsda_index_entry<P>); - if ( it->lsda != NULL ) { - LSDAEntry entry; - entry.func = it->func; - entry.lsda = it->lsda; - entry.lsdaOffset = it->lsdaOffset; - fLSDAIndex.push_back(entry); - } - } -} - -template <typename A> -void UnwindInfoAtom<A>::makePersonalityIndex(std::vector<Info>& uniqueInfos) -{ - for(typename std::vector<Info>::iterator it=uniqueInfos.begin(); it != uniqueInfos.end(); ++it) { - if ( it->personalityPointer != NULL ) { - std::map<ObjectFile::Atom*, uint32_t>::iterator pos = fPersonalityIndexMap.find(it->personalityPointer); - if ( pos == fPersonalityIndexMap.end() ) { - const uint32_t nextIndex = fPersonalityIndexMap.size() + 1; - fPersonalityIndexMap[it->personalityPointer] = nextIndex; - } - uint32_t personalityIndex = fPersonalityIndexMap[it->personalityPointer]; - it->encoding |= (personalityIndex << (__builtin_ctz(UNWIND_PERSONALITY_MASK)) ); - } - } -} - -template <typename A> -unsigned int UnwindInfoAtom<A>::makeRegularSecondLevelPage(const std::vector<Info>& uniqueInfos, uint32_t pageSize, - unsigned int endIndex, uint8_t*& pageEnd) -{ - const unsigned int maxEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - const unsigned int entriesToAdd = ((endIndex > maxEntriesPerPage) ? maxEntriesPerPage : endIndex); - uint8_t* pageStart = pageEnd - - entriesToAdd*sizeof(unwind_info_regular_second_level_entry) - - sizeof(unwind_info_regular_second_level_page_header); - macho_unwind_info_regular_second_level_page_header<P>* page = (macho_unwind_info_regular_second_level_page_header<P>*)pageStart; - page->set_kind(UNWIND_SECOND_LEVEL_REGULAR); - page->set_entryPageOffset(sizeof(macho_unwind_info_regular_second_level_page_header<P>)); - page->set_entryCount(entriesToAdd); - macho_unwind_info_regular_second_level_entry<P>* entryTable = (macho_unwind_info_regular_second_level_entry<P>*)(pageStart + page->entryPageOffset()); - for (unsigned int i=0; i < entriesToAdd; ++i) { - const Info& info = uniqueInfos[endIndex-entriesToAdd+i]; - entryTable[i].set_functionOffset(0); - entryTable[i].set_encoding(info.encoding); - RegFixUp fixup; - fixup.contentPointer = (uint8_t*)(&entryTable[i]); - fixup.func = info.func; - fixup.fde = ( encodingMeansUseDwarf(info.encoding) ? info.fde : NULL ); - fRegFixUps.push_back(fixup); - } - //fprintf(stderr, "regular page with %u entries\n", entriesToAdd); - pageEnd = pageStart; - return endIndex - entriesToAdd; -} - - -template <typename A> -unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<Info>& uniqueInfos, - const std::map<uint32_t,unsigned int> commonEncodings, - uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd) -{ - const bool log = false; - if (log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex); - // first pass calculates how many compressed entries we could fit in this sized page - // keep adding entries to page until: - // 1) encoding table plus entry table plus header exceed page size - // 2) the file offset delta from the first to last function > 24 bits - // 3) custom encoding index reachs 255 - // 4) run out of uniqueInfos to encode - std::map<uint32_t, unsigned int> pageSpecificEncodings; - uint32_t space4 = (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t); - std::vector<uint8_t> encodingIndexes; - int index = endIndex-1; - int entryCount = 0; - uint64_t lastEntryAddress = uniqueInfos[index].func->getAddress(); - bool canDo = true; - while ( canDo && (index >= 0) ) { - const Info& info = uniqueInfos[index--]; - // compute encoding index - unsigned int encodingIndex; - std::map<uint32_t, unsigned int>::const_iterator pos = commonEncodings.find(info.encoding); - if ( pos != commonEncodings.end() ) { - encodingIndex = pos->second; - } - else { - // no commmon entry, so add one on this page - uint32_t encoding = info.encoding; - if ( encodingMeansUseDwarf(encoding) ) { - // make unique pseudo encoding so this dwarf will gets is own encoding entry slot - encoding += (index+1); - } - std::map<uint32_t, unsigned int>::iterator ppos = pageSpecificEncodings.find(encoding); - if ( ppos != pageSpecificEncodings.end() ) { - encodingIndex = pos->second; - } - else { - encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); - if ( encodingIndex <= 255 ) { - pageSpecificEncodings[encoding] = encodingIndex; - } - else { - canDo = false; // case 3) - if (log) fprintf(stderr, "end of compressed page with %u entries, %lu custom encodings because too many custom encodings\n", - entryCount, pageSpecificEncodings.size()); - } - } - } - if ( canDo ) - encodingIndexes.push_back(encodingIndex); - // compute function offset - uint32_t funcOffsetWithInPage = lastEntryAddress - info.func->getAddress(); - if ( funcOffsetWithInPage > 0x00FFFF00 ) { - // don't use 0x00FFFFFF because addresses may vary after atoms are laid out again - canDo = false; // case 2) - if (log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount); - } - else { - ++entryCount; - } - // check room for entry - if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { - canDo = false; // case 1) - --entryCount; - if (log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); - } - //if (log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); - } - - // check for cases where it would be better to use a regular (non-compressed) page - const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) - + pageSpecificEncodings.size()*sizeof(uint32_t) - + entryCount*sizeof(uint32_t); - if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) { - const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - if ( entryCount < regularEntriesPerPage ) { - return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); - } - } - - // check if we need any padding because adding another entry would take 8 bytes but only have room for 4 - uint32_t pad = 0; - if ( compressPageUsed == (pageSize-4) ) - pad = 4; - - // second pass fills in page - uint8_t* pageStart = pageEnd - compressPageUsed - pad; - macho_unwind_info_compressed_second_level_page_header<P>* page = (macho_unwind_info_compressed_second_level_page_header<P>*)pageStart; - page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED); - page->set_entryPageOffset(sizeof(macho_unwind_info_compressed_second_level_page_header<P>)); - page->set_entryCount(entryCount); - page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t)); - page->set_encodingsCount(pageSpecificEncodings.size()); - uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; - // fill in entry table - uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()]; - ObjectFile::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func; - for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) { - const Info& info = uniqueInfos[i]; - uint8_t encodingIndex; - if ( encodingMeansUseDwarf(info.encoding) ) { - // dwarf entries are always in page specific encodings - encodingIndex = pageSpecificEncodings[info.encoding+i]; - } - else { - std::map<uint32_t, unsigned int>::const_iterator pos = commonEncodings.find(info.encoding); - if ( pos != commonEncodings.end() ) - encodingIndex = pos->second; - else - encodingIndex = pageSpecificEncodings[info.encoding]; - } - uint32_t entryIndex = i - endIndex + entryCount; - A::P::E::set32(entiresArray[entryIndex], encodingIndex << 24); - CompressedFixUp funcStartFixUp; - funcStartFixUp.contentPointer = (uint8_t*)(&entiresArray[entryIndex]); - funcStartFixUp.func = info.func; - funcStartFixUp.fromFunc = firstFunc; - fCompressedFixUps.push_back(funcStartFixUp); - if ( encodingMeansUseDwarf(info.encoding) ) { - CompressedEncodingFixUp dwarfStartFixup; - dwarfStartFixup.contentPointer = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]); - dwarfStartFixup.fde = info.fde; - fCompressedEncodingFixUps.push_back(dwarfStartFixup); - } - } - // fill in encodings table - for(std::map<uint32_t, unsigned int>::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { - A::P::E::set32(encodingsArray[it->second-commonEncodings.size()], it->first); - } - - if (log) fprintf(stderr, "compressed page with %u entries, %lu custom encodings\n", entryCount, pageSpecificEncodings.size()); - - // update pageEnd; - pageEnd = pageStart; - return endIndex-entryCount; // endIndex for next page -} - -template <> void UnwindInfoAtom<ppc>::generate() { } -template <> void UnwindInfoAtom<ppc64>::generate() { } -template <> void UnwindInfoAtom<arm>::generate() { } - - -template <typename A> -void UnwindInfoAtom<A>::generate() -{ - // only generate table if there are functions with unwind info - if ( fInfos.size() > 0 ) { - // find offset of end of __unwind_info section - SectionInfo* unwindSectionInfo = (SectionInfo*)this->getSection(); - - // build new list that has proper offsetInImage and remove entries where next function has same encoding - std::vector<Info> uniqueInfos; - this->compressDuplicates(uniqueInfos); - - // build personality index, update encodings with personality index - this->makePersonalityIndex(uniqueInfos); - if ( fPersonalityIndexMap.size() > 3 ) - throw "too many personality routines for compact unwind to encode"; - - // put the most common encodings into the common table, but at most 127 of them - std::map<uint32_t, unsigned int> commonEncodings; - this->findCommonEncoding(uniqueInfos, commonEncodings); - - // build lsda index - std::map<ObjectFile::Atom*, uint32_t> lsdaIndexOffsetMap; - this->makeLsdaIndex(uniqueInfos, lsdaIndexOffsetMap); - - // calculate worst case size for all unwind info pages when allocating buffer - const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); - const unsigned int pageCount = ((uniqueInfos.size() - 1)/entriesPerRegularPage) + 1; - fPagesContentForDelete = (uint8_t*)calloc(pageCount,4096); - fPagesSize = 0; - if ( fPagesContentForDelete == NULL ) - throw "could not allocate space for compact unwind info"; - ObjectFile::Atom* secondLevelFirstFuncs[pageCount*3]; - uint8_t* secondLevelPagesStarts[pageCount*3]; - - // make last second level page smaller so that all other second level pages can be page aligned - uint32_t maxLastPageSize = unwindSectionInfo->fFileOffset % 4096; - uint32_t tailPad = 0; - if ( maxLastPageSize < 128 ) { - tailPad = maxLastPageSize; - maxLastPageSize = 4096; - } - - // fill in pages in reverse order - unsigned int endIndex = uniqueInfos.size(); - unsigned int secondLevelPageCount = 0; - uint8_t* pageEnd = &fPagesContentForDelete[pageCount*4096]; - uint32_t pageSize = maxLastPageSize; - while ( endIndex > 0 ) { - endIndex = makeCompressedSecondLevelPage(uniqueInfos, commonEncodings, pageSize, endIndex, pageEnd); - secondLevelPagesStarts[secondLevelPageCount] = pageEnd; - secondLevelFirstFuncs[secondLevelPageCount] = uniqueInfos[endIndex].func; - ++secondLevelPageCount; - pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size - } - fPagesContent = pageEnd; - fPagesSize = &fPagesContentForDelete[pageCount*4096] - pageEnd; - - // calculate section layout - const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header<P>); - const uint32_t commonEncodingsArrayCount = commonEncodings.size(); - const uint32_t commonEncodingsArraySize = commonEncodingsArrayCount * sizeof(compact_unwind_encoding_t); - const uint32_t personalityArraySectionOffset = commonEncodingsArraySectionOffset + commonEncodingsArraySize; - const uint32_t personalityArrayCount = fPersonalityIndexMap.size(); - const uint32_t personalityArraySize = personalityArrayCount * sizeof(uint32_t); - const uint32_t indexSectionOffset = personalityArraySectionOffset + personalityArraySize; - const uint32_t indexCount = secondLevelPageCount+1; - const uint32_t indexSize = indexCount * sizeof(macho_unwind_info_section_header_index_entry<P>); - const uint32_t lsdaIndexArraySectionOffset = indexSectionOffset + indexSize; - const uint32_t lsdaIndexArrayCount = fLSDAIndex.size(); - const uint32_t lsdaIndexArraySize = lsdaIndexArrayCount * sizeof(macho_unwind_info_section_header_lsda_index_entry<P>); - const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize; - - - // allocate and fill in section header - fHeaderSize = headerEndSectionOffset; - fHeaderContent = new uint8_t[fHeaderSize]; - bzero(fHeaderContent, fHeaderSize); - macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)fHeaderContent; - sectionHeader->set_version(UNWIND_SECTION_VERSION); - sectionHeader->set_commonEncodingsArraySectionOffset(commonEncodingsArraySectionOffset); - sectionHeader->set_commonEncodingsArrayCount(commonEncodingsArrayCount); - sectionHeader->set_personalityArraySectionOffset(personalityArraySectionOffset); - sectionHeader->set_personalityArrayCount(personalityArrayCount); - sectionHeader->set_indexSectionOffset(indexSectionOffset); - sectionHeader->set_indexCount(indexCount); - - // copy common encodings - uint32_t* commonEncodingsTable = (uint32_t*)&fHeaderContent[commonEncodingsArraySectionOffset]; - for (std::map<uint32_t, unsigned int>::iterator it=commonEncodings.begin(); it != commonEncodings.end(); ++it) - A::P::E::set32(commonEncodingsTable[it->second], it->first); - - // make references for personality entries - uint32_t* personalityArray = (uint32_t*)&fHeaderContent[sectionHeader->personalityArraySectionOffset()]; - for (std::map<ObjectFile::Atom*, unsigned int>::iterator it=fPersonalityIndexMap.begin(); it != fPersonalityIndexMap.end(); ++it) { - uint32_t offset = (uint8_t*)&personalityArray[it->second-1] - fHeaderContent; - fReferences.push_back(new WriterReference<A>(offset, A::kImageOffset32, it->first)); - } - - // build first level index and references - macho_unwind_info_section_header_index_entry<P>* indexTable = (macho_unwind_info_section_header_index_entry<P>*)&fHeaderContent[indexSectionOffset]; - for (unsigned int i=0; i < secondLevelPageCount; ++i) { - unsigned int reverseIndex = secondLevelPageCount - 1 - i; - indexTable[i].set_functionOffset(0); - indexTable[i].set_secondLevelPagesSectionOffset(secondLevelPagesStarts[reverseIndex]-fPagesContent+headerEndSectionOffset); - indexTable[i].set_lsdaIndexArraySectionOffset(lsdaIndexOffsetMap[secondLevelFirstFuncs[reverseIndex]]+lsdaIndexArraySectionOffset); - uint32_t refOffset = (uint8_t*)&indexTable[i] - fHeaderContent; - fReferences.push_back(new WriterReference<A>(refOffset, A::kImageOffset32, secondLevelFirstFuncs[reverseIndex])); - } - indexTable[secondLevelPageCount].set_functionOffset(0); - indexTable[secondLevelPageCount].set_secondLevelPagesSectionOffset(0); - indexTable[secondLevelPageCount].set_lsdaIndexArraySectionOffset(lsdaIndexArraySectionOffset+lsdaIndexArraySize); - fReferences.push_back(new WriterReference<A>((uint8_t*)&indexTable[secondLevelPageCount] - fHeaderContent, A::kImageOffset32, - fInfos.back().func, fInfos.back().func->getSize()+1)); - - // build lsda references - uint32_t lsdaEntrySectionOffset = lsdaIndexArraySectionOffset; - for (typename std::vector<LSDAEntry>::iterator it = fLSDAIndex.begin(); it != fLSDAIndex.end(); ++it) { - fReferences.push_back(new WriterReference<A>(lsdaEntrySectionOffset, A::kImageOffset32, it->func)); - fReferences.push_back(new WriterReference<A>(lsdaEntrySectionOffset+4, A::kImageOffset32, it->lsda, it->lsdaOffset)); - lsdaEntrySectionOffset += sizeof(unwind_info_section_header_lsda_index_entry); - } - - // make references for regular second level entries - for (typename std::vector<RegFixUp>::iterator it = fRegFixUps.begin(); it != fRegFixUps.end(); ++it) { - uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; - fReferences.push_back(new WriterReference<A>(offset, A::kImageOffset32, it->func)); - if ( it->fde != NULL ) - fReferences.push_back(new WriterReference<A>(offset+4, A::kSectionOffset24, it->fde)); - } - // make references for compressed second level entries - for (typename std::vector<CompressedFixUp>::iterator it = fCompressedFixUps.begin(); it != fCompressedFixUps.end(); ++it) { - uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; - fReferences.push_back(new WriterReference<A>(offset, A::kPointerDiff24, it->func, 0, it->fromFunc, 0)); - } - for (typename std::vector<CompressedEncodingFixUp>::iterator it = fCompressedEncodingFixUps.begin(); it != fCompressedEncodingFixUps.end(); ++it) { - uint32_t offset = (it->contentPointer - fPagesContent) + fHeaderSize; - fReferences.push_back(new WriterReference<A>(offset, A::kSectionOffset24, it->fde)); - } - - // update section record with new size - unwindSectionInfo->fSize = this->getSize(); - - // alter alignment so this section lays out so second level tables are page aligned - if ( secondLevelPageCount > 2 ) - fAlignment = ObjectFile::Alignment(12, (unwindSectionInfo->fFileOffset - this->getSize()) % 4096); - } - -} - - - - -template <typename A> -void UnwindInfoAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fHeaderContent, fHeaderSize); - memcpy(&buffer[fHeaderSize], fPagesContent, fPagesSize); -} - - - -template <typename A> -uint64_t LinkEditAtom<A>::getFileOffset() const -{ - return ((SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset(); -} - - -template <typename A> -uint64_t SectionRelocationsLinkEditAtom<A>::getSize() const -{ - return fWriter.fSectionRelocs.size() * sizeof(macho_relocation_info<P>); -} - -template <typename A> -void SectionRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fWriter.fSectionRelocs[0], this->getSize()); -} - - -template <typename A> -uint64_t LocalRelocationsLinkEditAtom<A>::getSize() const -{ - return fWriter.fInternalRelocs.size() * sizeof(macho_relocation_info<P>); -} - -template <typename A> -void LocalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fWriter.fInternalRelocs[0], this->getSize()); -} - - - -template <typename A> -uint64_t SymbolTableLinkEditAtom<A>::getSize() const -{ - return fWriter.fSymbolTableCount * sizeof(macho_nlist<P>); -} - -template <typename A> -void SymbolTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fWriter.fSymbolTable, this->getSize()); -} - -template <typename A> -uint64_t ExternalRelocationsLinkEditAtom<A>::getSize() const -{ - return fWriter.fExternalRelocs.size() * sizeof(macho_relocation_info<P>); -} - -template <typename A> -void ExternalRelocationsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - std::sort(fWriter.fExternalRelocs.begin(), fWriter.fExternalRelocs.end(), ExternalRelocSorter<P>()); - memcpy(buffer, &fWriter.fExternalRelocs[0], this->getSize()); -} - - - -template <typename A> -uint64_t IndirectTableLinkEditAtom<A>::getSize() const -{ - return fTable.size() * sizeof(uint32_t); -} - -template <typename A> -void IndirectTableLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - const uint32_t indirectTableSize = fTable.size(); - uint32_t* indirectTable = (uint32_t*)buffer; - for(std::vector<IndirectEntry>::const_iterator it = fTable.begin(); it != fTable.end(); ++it) { - if ( it->indirectIndex < indirectTableSize ) - A::P::E::set32(indirectTable[it->indirectIndex], it->symbolIndex); - else - throwf("malformed indirect table. size=%d, index=%d", indirectTableSize, it->indirectIndex); - } -} - - - -template <typename A> -uint64_t ModuleInfoLinkEditAtom<A>::getSize() const -{ - return fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>) - + sizeof(macho_dylib_module<P>) - + this->getReferencesCount()*sizeof(uint32_t); -} - -template <typename A> -uint32_t ModuleInfoLinkEditAtom<A>::getTableOfContentsFileOffset() const -{ - return this->getFileOffset(); -} - -template <typename A> -uint32_t ModuleInfoLinkEditAtom<A>::getModuleTableFileOffset() const -{ - return this->getFileOffset() + fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>); -} - -template <typename A> -uint32_t ModuleInfoLinkEditAtom<A>::getReferencesFileOffset() const -{ - return this->getModuleTableFileOffset() + sizeof(macho_dylib_module<P>); -} - -template <typename A> -uint32_t ModuleInfoLinkEditAtom<A>::getReferencesCount() const -{ - return fWriter.fSymbolTableExportCount + fWriter.fSymbolTableImportCount; -} - -template <typename A> -void ModuleInfoLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - bzero(buffer, size); - // create toc. The symbols are already sorted, they are all in the smae module - macho_dylib_table_of_contents<P>* p = (macho_dylib_table_of_contents<P>*)buffer; - for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++p) { - p->set_symbol_index(fWriter.fSymbolTableExportStartIndex+i); - p->set_module_index(0); - } - // create module table (one entry) - pint_t objcModuleSectionStart = 0; - pint_t objcModuleSectionSize = 0; - uint16_t numInits = 0; - uint16_t numTerms = 0; - std::vector<SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos; - for (std::vector<SegmentInfo*>::iterator segit = segmentInfos.begin(); segit != segmentInfos.end(); ++segit) { - std::vector<SectionInfo*>& sectionInfos = (*segit)->fSections; - if ( strcmp((*segit)->fName, "__DATA") == 0 ) { - for (std::vector<SectionInfo*>::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { - if ( strcmp((*sectit)->fSectionName, "__mod_init_func") == 0 ) - numInits = (*sectit)->fSize / sizeof(typename A::P::uint_t); - else if ( strcmp((*sectit)->fSectionName, "__mod_term_func") == 0 ) - numTerms = (*sectit)->fSize / sizeof(typename A::P::uint_t); - } - } - else if ( strcmp((*segit)->fName, "__OBJC") == 0 ) { - for (std::vector<SectionInfo*>::iterator sectit = sectionInfos.begin(); sectit != sectionInfos.end(); ++sectit) { - SectionInfo* sectInfo = (*sectit); - if ( strcmp(sectInfo->fSectionName, "__module_info") == 0 ) { - objcModuleSectionStart = sectInfo->getBaseAddress(); - objcModuleSectionSize = sectInfo->fSize; - } - } - } - } - macho_dylib_module<P>* module = (macho_dylib_module<P>*)&buffer[fWriter.fSymbolTableExportCount*sizeof(macho_dylib_table_of_contents<P>)]; - module->set_module_name(fModuleNameOffset); - module->set_iextdefsym(fWriter.fSymbolTableExportStartIndex); - module->set_nextdefsym(fWriter.fSymbolTableExportCount); - module->set_irefsym(0); - module->set_nrefsym(this->getReferencesCount()); - module->set_ilocalsym(fWriter.fSymbolTableStabsStartIndex); - module->set_nlocalsym(fWriter.fSymbolTableStabsCount+fWriter.fSymbolTableLocalCount); - module->set_iextrel(0); - module->set_nextrel(fWriter.fExternalRelocs.size()); - module->set_iinit_iterm(0,0); - module->set_ninit_nterm(numInits,numTerms); - module->set_objc_module_info_addr(objcModuleSectionStart); - module->set_objc_module_info_size(objcModuleSectionSize); - // create reference table - macho_dylib_reference<P>* ref = (macho_dylib_reference<P>*)((uint8_t*)module + sizeof(macho_dylib_module<P>)); - for(uint32_t i=0; i < fWriter.fSymbolTableExportCount; ++i, ++ref) { - ref->set_isym(fWriter.fSymbolTableExportStartIndex+i); - ref->set_flags(REFERENCE_FLAG_DEFINED); - } - for(uint32_t i=0; i < fWriter.fSymbolTableImportCount; ++i, ++ref) { - ref->set_isym(fWriter.fSymbolTableImportStartIndex+i); - std::map<const ObjectFile::Atom*,ObjectFile::Atom*>::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 <typename A> -StringsLinkEditAtom<A>::StringsLinkEditAtom(Writer<A>& writer) - : LinkEditAtom<A>(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 <typename A> -uint64_t StringsLinkEditAtom<A>::getSize() const -{ - // align size - return (kBufferSize * fFullBuffers.size() + fCurrentBufferUsed + sizeof(typename A::P::uint_t) - 1) & (-sizeof(typename A::P::uint_t)); -} - -template <typename A> -void StringsLinkEditAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t offset = 0; - for (unsigned int i=0; i < fFullBuffers.size(); ++i) { - memcpy(&buffer[offset], fFullBuffers[i], kBufferSize); - offset += kBufferSize; - } - memcpy(&buffer[offset], fCurrentBuffer, fCurrentBufferUsed); - // zero fill end to align - offset += fCurrentBufferUsed; - while ( (offset % sizeof(typename A::P::uint_t)) != 0 ) - buffer[offset++] = 0; -} - -template <typename A> -int32_t StringsLinkEditAtom<A>::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 <typename A> -int32_t StringsLinkEditAtom<A>::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 <typename A> -const char* StringsLinkEditAtom<A>::stringForIndex(int32_t index) const -{ - int32_t currentBufferStartIndex = kBufferSize * fFullBuffers.size(); - int32_t maxIndex = currentBufferStartIndex + fCurrentBufferUsed; - // check for out of bounds - if ( index > maxIndex ) - return ""; - // check for index in fCurrentBuffer - if ( index > currentBufferStartIndex ) - return &fCurrentBuffer[index-currentBufferStartIndex]; - // otherwise index is in a full buffer - uint32_t fullBufferIndex = index/kBufferSize; - return &fFullBuffers[fullBufferIndex][index-(kBufferSize*fullBufferIndex)]; -} - - - -template <typename A> -BranchIslandAtom<A>::BranchIslandAtom(Writer<A>& writer, const char* name, int islandRegion, ObjectFile::Atom& target, - ObjectFile::Atom& finalTarget, uint32_t finalTargetOffset) - : WriterAtom<A>(writer, Segment::fgTextSegment), fTarget(target), fFinalTarget(finalTarget), fFinalTargetOffset(finalTargetOffset) -{ - if ( finalTargetOffset == 0 ) { - if ( islandRegion == 0 ) - asprintf((char**)&fName, "%s$island", name); - else - asprintf((char**)&fName, "%s$island$%d", name, islandRegion+1); - } - else { - asprintf((char**)&fName, "%s_plus_%d$island$%d", name, finalTargetOffset, islandRegion); - } - - if ( finalTarget.isThumb() ) { - if ( writer.fOptions.preferSubArchitecture() && writer.fOptions.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { - fIslandKind = kBranchIslandToThumb2; - } - else { - if ( writer.fSlideable ) - fIslandKind = kBranchIslandToThumb1; - else - fIslandKind = kBranchIslandNoPicToThumb1; - } - } - else { - fIslandKind = kBranchIslandToARM; - } -} - - -template <> -void BranchIslandAtom<ppc>::copyRawContent(uint8_t buffer[]) const -{ - int64_t displacement; - const int64_t bl_sixteenMegLimit = 0x00FFFFFF; - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress(); - if ( (displacement > bl_sixteenMegLimit) && (displacement < (-bl_sixteenMegLimit)) ) { - displacement = fTarget.getAddress() - this->getAddress(); - } - } - else { - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress(); - } - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(buffer, 0, branchInstruction); -} - -template <> -void BranchIslandAtom<ppc64>::copyRawContent(uint8_t buffer[]) const -{ - int64_t displacement; - const int64_t bl_sixteenMegLimit = 0x00FFFFFF; - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress(); - if ( (displacement > bl_sixteenMegLimit) && (displacement < (-bl_sixteenMegLimit)) ) { - displacement = fTarget.getAddress() - this->getAddress(); - } - } - else { - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress(); - } - int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); - OSWriteBigInt32(buffer, 0, branchInstruction); -} - -template <> -void BranchIslandAtom<arm>::copyRawContent(uint8_t buffer[]) const -{ - const bool log = false; - switch ( fIslandKind ) { - case kBranchIslandToARM: - { - int64_t displacement; - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress() - 8; - if ( (displacement < 33554428LL) && (displacement > (-33554432LL)) ) { - // can skip branch island and jump straight to target - if (log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", fName, getFinalTargetAdress(), this->getAddress()); - } - else { - // ultimate target is too far, jump to island - displacement = fTarget.getAddress() - this->getAddress() - 8; - if (log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", fName, fTarget.getAddress()); - } - } - else { - // target of island is ultimate target - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress() - 8; - if (log) fprintf(stderr, "%s: jump to target at 0x%08llX\n", fName, fTarget.getAddress()); - } - uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF; - int32_t branchInstruction = 0xEA000000 | imm24; - OSWriteLittleInt32(buffer, 0, branchInstruction); - } - break; - case kBranchIslandToThumb2: - { - int64_t displacement; - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - if ( fTarget.getContentType() == ObjectFile::Atom::kBranchIsland ) { - displacement = getFinalTargetAdress() - this->getAddress() - 4; - if ( (displacement < 16777214) && (displacement > (-16777216LL)) ) { - // can skip branch island and jump straight to target - if (log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", fName, getFinalTargetAdress(), this->getAddress()); - } - else { - // ultimate target is too far, jump to island - displacement = fTarget.getAddress() - this->getAddress() - 4; - if (log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", fName, fTarget.getAddress()); - } - } - else { - // target of island is ultimate target - displacement = fTarget.getAddress() + fFinalTargetOffset - this->getAddress() - 4; - if (log) fprintf(stderr, "%s: jump to target at 0x%08llX\n", fName, fTarget.getAddress()); - } - if ( (displacement > 16777214) || (displacement < (-16777216LL)) ) { - throwf("internal branch island error: thumb2 b/bx out of range (%lld max is +/-16M) from %s to %s in %s", - displacement, this->getDisplayName(), - fTarget.getDisplayName(), fTarget.getFile()->getPath()); - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - uint32_t opcode = 0x9000F000; - uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; - uint32_t firstDisp = (s << 10) | imm10; - uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - OSWriteLittleInt32(buffer, 0, newInstruction); - } - break; - case kBranchIslandToThumb1: - { - // There is no large displacement thumb1 branch instruction. - // Instead use ARM instructions that can jump to thumb. - // we use a 32-bit displacement, so we can directly jump to target which means no island hopping - int64_t displacement = getFinalTargetAdress() - (this->getAddress() + 12); - if ( fFinalTarget.isThumb() ) - displacement |= 1; - if (log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", fName, getFinalTargetAdress()); - OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 - OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip - OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip - OSWriteLittleInt32(&buffer[12], 0, displacement); // .long target-this - } - break; - case kBranchIslandNoPicToThumb1: - { - // There is no large displacement thumb1 branch instruction. - // Instead use ARM instructions that can jump to thumb. - // we use a 32-bit displacement, so we can directly jump to target which means no island hopping - uint32_t targetAddr = getFinalTargetAdress(); - if ( fFinalTarget.isThumb() ) - targetAddr |= 1; - if (log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", fName, getFinalTargetAdress()); - OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] - OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this - } - break; - }; -} - -template <> -uint64_t BranchIslandAtom<ppc>::getSize() const -{ - return 4; -} - -template <> -uint64_t BranchIslandAtom<ppc64>::getSize() const -{ - return 4; -} - -template <> -uint64_t BranchIslandAtom<arm>::getSize() const -{ - switch ( fIslandKind ) { - case kBranchIslandToARM: - return 4; - case kBranchIslandToThumb1: - return 16; - case kBranchIslandToThumb2: - return 4; - case kBranchIslandNoPicToThumb1: - return 8; - }; - throw "internal error: no ARM branch island kind"; -} - - - -template <typename A> -uint64_t SegmentSplitInfoLoadCommandsAtom<A>::getSize() const -{ - if ( fWriter.fSplitCodeToDataContentAtom->canEncode() ) - return this->alignedSize(sizeof(macho_linkedit_data_command<P>)); - else - return 0; // a zero size causes the load command to be suppressed -} - -template <typename A> -void SegmentSplitInfoLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - uint64_t size = this->getSize(); - if ( size > 0 ) { - bzero(buffer, size); - macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)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 <typename A> -uint64_t SegmentSplitInfoContentAtom<A>::getSize() const -{ - return fEncodedData.size(); -} - -template <typename A> -void SegmentSplitInfoContentAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fEncodedData[0], fEncodedData.size()); -} - - -template <typename A> -void SegmentSplitInfoContentAtom<A>::uleb128EncodeAddresses(const std::vector<SegmentSplitInfoContentAtom<A>::AtomAndOffset>& locations) -{ - pint_t addr = fWriter.fOptions.baseAddress(); - for(typename std::vector<AtomAndOffset>::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 <typename A> -void SegmentSplitInfoContentAtom<A>::encode() -{ - if ( ! fCantEncode ) { - fEncodedData.reserve(8192); - - if ( fKind1Locations.size() != 0 ) { - fEncodedData.push_back(1); - //fprintf(stderr, "type 1:\n"); - this->uleb128EncodeAddresses(fKind1Locations); - fEncodedData.push_back(0); - } - - if ( fKind2Locations.size() != 0 ) { - fEncodedData.push_back(2); - //fprintf(stderr, "type 2:\n"); - this->uleb128EncodeAddresses(fKind2Locations); - fEncodedData.push_back(0); - } - - if ( fKind3Locations.size() != 0 ) { - fEncodedData.push_back(3); - //fprintf(stderr, "type 3:\n"); - this->uleb128EncodeAddresses(fKind3Locations); - fEncodedData.push_back(0); - } - - if ( fKind4Locations.size() != 0 ) { - fEncodedData.push_back(4); - //fprintf(stderr, "type 4:\n"); - this->uleb128EncodeAddresses(fKind4Locations); - fEncodedData.push_back(0); - } - - // always add zero byte to mark end - fEncodedData.push_back(0); - - // add zeros to end to align size - while ( (fEncodedData.size() % sizeof(pint_t)) != 0 ) - fEncodedData.push_back(0); - } -} - - -template <typename A> -ObjCInfoAtom<A>::ObjCInfoAtom(Writer<A>& writer, ObjectFile::Reader::ObjcConstraint objcConstraint, - bool objcReplacementClasses, bool abi2override) - : WriterAtom<A>(writer, getInfoSegment(abi2override)), fAbi2override(abi2override) -{ - fContent[0] = 0; - uint32_t value = 0; - // struct objc_image_info { - // uint32_t version; // initially 0 - // uint32_t flags; - // }; - // #define OBJC_IMAGE_SUPPORTS_GC 2 - // #define OBJC_IMAGE_GC_ONLY 4 - // - if ( objcReplacementClasses ) - value = 1; - switch ( objcConstraint ) { - case ObjectFile::Reader::kObjcNone: - case ObjectFile::Reader::kObjcRetainRelease: - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - value |= 2; - break; - case ObjectFile::Reader::kObjcGC: - value |= 6; - break; - } - A::P::E::set32(fContent[1], value); -} - -template <typename A> -void ObjCInfoAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, &fContent[0], 8); -} - - -// objc info section is in a different segment and section for 32 vs 64 bit runtimes -template <> const char* ObjCInfoAtom<ppc>::getSectionName() const { return "__image_info"; } -template <> const char* ObjCInfoAtom<x86>::getSectionName() const { return fAbi2override ? "__objc_imageinfo" : "__image_info"; } -template <> const char* ObjCInfoAtom<arm>::getSectionName() const { return "__objc_imageinfo"; } -template <> const char* ObjCInfoAtom<ppc64>::getSectionName() const { return "__objc_imageinfo"; } -template <> const char* ObjCInfoAtom<x86_64>::getSectionName() const { return "__objc_imageinfo"; } - -template <> Segment& ObjCInfoAtom<ppc>::getInfoSegment(bool abi2override) const { return Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom<x86>::getInfoSegment(bool abi2override) const { return abi2override ? Segment::fgDataSegment : Segment::fgObjCSegment; } -template <> Segment& ObjCInfoAtom<ppc64>::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom<x86_64>::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } -template <> Segment& ObjCInfoAtom<arm>::getInfoSegment(bool abi2override) const { return Segment::fgDataSegment; } - - - - -template <typename A> -void DyldInfoLoadCommandsAtom<A>::copyRawContent(uint8_t buffer[]) const -{ - // build LC_DYLD_INFO command - macho_dyld_info_command<P>* cmd = (macho_dyld_info_command<P>*)buffer; - bzero(cmd, sizeof(macho_dyld_info_command<P>)); - - cmd->set_cmd( fWriter.fOptions.makeClassicDyldInfo() ? LC_DYLD_INFO : LC_DYLD_INFO_ONLY); - cmd->set_cmdsize(sizeof(macho_dyld_info_command<P>)); - if ( (fWriter.fCompressedRebaseInfoAtom != NULL) && (fWriter.fCompressedRebaseInfoAtom->getSize() != 0) ) { - cmd->set_rebase_off(fWriter.fCompressedRebaseInfoAtom->getFileOffset()); - cmd->set_rebase_size(fWriter.fCompressedRebaseInfoAtom->getSize()); - } - if ( (fWriter.fCompressedBindingInfoAtom != NULL) && (fWriter.fCompressedBindingInfoAtom->getSize() != 0) ) { - cmd->set_bind_off(fWriter.fCompressedBindingInfoAtom->getFileOffset()); - cmd->set_bind_size(fWriter.fCompressedBindingInfoAtom->getSize()); - } - if ( (fWriter.fCompressedWeakBindingInfoAtom != NULL) && (fWriter.fCompressedWeakBindingInfoAtom->getSize() != 0) ) { - cmd->set_weak_bind_off(fWriter.fCompressedWeakBindingInfoAtom->getFileOffset()); - cmd->set_weak_bind_size(fWriter.fCompressedWeakBindingInfoAtom->getSize()); - } - if ( (fWriter.fCompressedLazyBindingInfoAtom != NULL) && (fWriter.fCompressedLazyBindingInfoAtom->getSize() != 0) ) { - cmd->set_lazy_bind_off(fWriter.fCompressedLazyBindingInfoAtom->getFileOffset()); - cmd->set_lazy_bind_size(fWriter.fCompressedLazyBindingInfoAtom->getSize()); - } - if ( (fWriter.fCompressedExportInfoAtom != NULL) && (fWriter.fCompressedExportInfoAtom->getSize() != 0) ) { - cmd->set_export_off(fWriter.fCompressedExportInfoAtom->getFileOffset()); - cmd->set_export_size(fWriter.fCompressedExportInfoAtom->getSize()); - } -} - - -struct rebase_tmp -{ - rebase_tmp(uint8_t op, uint64_t p1, uint64_t p2=0) : opcode(op), operand1(p1), operand2(p2) {} - uint8_t opcode; - uint64_t operand1; - uint64_t operand2; -}; - - -template <typename A> -void CompressedRebaseInfoLinkEditAtom<A>::encode() -{ - // sort rebase info by type, then address - const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos; - std::vector<RebaseInfo>& info = fWriter.fRebaseInfo; - std::sort(info.begin(), info.end()); - - // convert to temp encoding that can be more easily optimized - std::vector<rebase_tmp> mid; - const SegmentInfo* currentSegment = NULL; - unsigned int segIndex = 0; - uint8_t type = 0; - uint64_t address = (uint64_t)(-1); - for (std::vector<RebaseInfo>::iterator it = info.begin(); it != info.end(); ++it) { - if ( type != it->fType ) { - mid.push_back(rebase_tmp(REBASE_OPCODE_SET_TYPE_IMM, it->fType)); - type = it->fType; - } - if ( address != it->fAddress ) { - if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <= it->fAddress) ) { - segIndex = 0; - for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - mid.push_back(rebase_tmp(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); - } - else { - mid.push_back(rebase_tmp(REBASE_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); - } - address = it->fAddress; - } - mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); - address += sizeof(pint_t); - } - mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0)); - - // optimize phase 1, compress packed runs of pointers - rebase_tmp* dst = &mid[0]; - for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { - if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (src->operand1 == 1) ) { - *dst = *src++; - while (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES ) { - dst->operand1 += src->operand1; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = REBASE_OPCODE_DONE; - - // optimize phase 2, combine rebase/add pairs - dst = &mid[0]; - for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { - if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) - && (src->operand1 == 1) - && (src[1].opcode == REBASE_OPCODE_ADD_ADDR_ULEB)) { - dst->opcode = REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB; - dst->operand1 = src[1].operand1; - ++src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = REBASE_OPCODE_DONE; - - // optimize phase 3, compress packed runs of REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB with - // same addr delta into one REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB - dst = &mid[0]; - for (const rebase_tmp* src = &mid[0]; src->opcode != REBASE_OPCODE_DONE; ++src) { - uint64_t delta = src->operand1; - if ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src[1].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src[2].opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src[1].operand1 == delta) - && (src[2].operand1 == delta) ) { - // found at least three in a row, this is worth compressing - dst->opcode = REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB; - dst->operand1 = 1; - dst->operand2 = delta; - ++src; - while ( (src->opcode == REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB) - && (src->operand1 == delta) ) { - dst->operand1++; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = REBASE_OPCODE_DONE; - - // optimize phase 4, use immediate encodings - for (rebase_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { - if ( (p->opcode == REBASE_OPCODE_ADD_ADDR_ULEB) - && (p->operand1 < (15*sizeof(pint_t))) - && ((p->operand1 % sizeof(pint_t)) == 0) ) { - p->opcode = REBASE_OPCODE_ADD_ADDR_IMM_SCALED; - p->operand1 = p->operand1/sizeof(pint_t); - } - else if ( (p->opcode == REBASE_OPCODE_DO_REBASE_ULEB_TIMES) && (p->operand1 < 15) ) { - p->opcode = REBASE_OPCODE_DO_REBASE_IMM_TIMES; - } - } - - // convert to compressed encoding - const static bool log = false; - fEncodedData.reserve(info.size()*2); - bool done = false; - for (std::vector<rebase_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { - switch ( it->opcode ) { - case REBASE_OPCODE_DONE: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DONE()\n"); - done = true; - break; - case REBASE_OPCODE_SET_TYPE_IMM: - if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_SET_TYPE_IMM | it->operand1); - break; - case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); - fEncodedData.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - case REBASE_OPCODE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: - if ( log ) fprintf(stderr, "REBASE_OPCODE_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); - fEncodedData.append_byte(REBASE_OPCODE_ADD_ADDR_IMM_SCALED | it->operand1 ); - break; - case REBASE_OPCODE_DO_REBASE_IMM_TIMES: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_IMM_TIMES(%lld)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_IMM_TIMES | it->operand1); - break; - case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%lld)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES); - fEncodedData.append_uleb128(it->operand1); - break; - case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: - if ( log ) fprintf(stderr, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); - fEncodedData.append_byte(REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB); - fEncodedData.append_uleb128(it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - } - } - - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - if (log) fprintf(stderr, "total rebase info size = %ld\n", fEncodedData.size()); -} - - -struct binding_tmp -{ - binding_tmp(uint8_t op, uint64_t p1, uint64_t p2=0, const char* s=NULL) - : opcode(op), operand1(p1), operand2(p2), name(s) {} - uint8_t opcode; - uint64_t operand1; - uint64_t operand2; - const char* name; -}; - - - -template <typename A> -void CompressedBindingInfoLinkEditAtom<A>::encode() -{ - // sort by library, symbol, type, then address - const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos; - std::vector<BindingInfo>& info = fWriter.fBindingInfo; - std::sort(info.begin(), info.end()); - - // convert to temp encoding that can be more easily optimized - std::vector<binding_tmp> mid; - const SegmentInfo* currentSegment = NULL; - unsigned int segIndex = 0; - int ordinal = 0x80000000; - const char* symbolName = NULL; - uint8_t type = 0; - uint64_t address = (uint64_t)(-1); - int64_t addend = 0; - for (std::vector<BindingInfo>::iterator it = info.begin(); it != info.end(); ++it) { - if ( ordinal != it->fLibraryOrdinal ) { - if ( it->fLibraryOrdinal <= 0 ) { - // special lookups are encoded as negative numbers in BindingInfo - mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, it->fLibraryOrdinal)); - } - else { - mid.push_back(binding_tmp(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB, it->fLibraryOrdinal)); - } - ordinal = it->fLibraryOrdinal; - } - if ( symbolName != it->fSymbolName ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName)); - symbolName = it->fSymbolName; - } - if ( type != it->fType ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType)); - type = it->fType; - } - if ( address != it->fAddress ) { - if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress) - || (it->fAddress < address) ) { - segIndex = 0; - for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); - } - else { - mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); - } - address = it->fAddress; - } - if ( addend != it->fAddend ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend)); - addend = it->fAddend; - } - mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); - address += sizeof(pint_t); - } - mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); - - - // optimize phase 1, combine bind/add pairs - binding_tmp* dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - if ( (src->opcode == BIND_OPCODE_DO_BIND) - && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { - dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; - dst->operand1 = src[1].operand1; - ++src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with - // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB - dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - uint64_t delta = src->operand1; - if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].operand1 == delta) ) { - // found at least two in a row, this is worth compressing - dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; - dst->operand1 = 1; - dst->operand2 = delta; - ++src; - while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src->operand1 == delta) ) { - dst->operand1++; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 3, use immediate encodings - for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { - if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (p->operand1 < (15*sizeof(pint_t))) - && ((p->operand1 % sizeof(pint_t)) == 0) ) { - p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; - p->operand1 = p->operand1/sizeof(pint_t); - } - else if ( (p->opcode == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB) && (p->operand1 <= 15) ) { - p->opcode = BIND_OPCODE_SET_DYLIB_ORDINAL_IMM; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // convert to compressed encoding - const static bool log = false; - fEncodedData.reserve(info.size()*2); - bool done = false; - for (std::vector<binding_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { - switch ( it->opcode ) { - case BIND_OPCODE_DONE: - if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); - done = true; - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); - break; - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); - fEncodedData.append_string(it->name); - break; - case BIND_OPCODE_SET_TYPE_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); - break; - case BIND_OPCODE_SET_ADDEND_SLEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); - fEncodedData.append_sleb128(it->operand1); - break; - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - case BIND_OPCODE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); - break; - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); - fEncodedData.append_uleb128(it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - } - } - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - if (log) fprintf(stderr, "total binding info size = %ld\n", fEncodedData.size()); - -} - - - -struct WeakBindingSorter -{ - bool operator()(const BindingInfo& left, const BindingInfo& right) - { - // sort by symbol, type, address - if ( left.fSymbolName != right.fSymbolName ) - return ( strcmp(left.fSymbolName, right.fSymbolName) < 0 ); - if ( left.fType != right.fType ) - return (left.fType < right.fType); - return (left.fAddress < right.fAddress); - } -}; - - - -template <typename A> -void CompressedWeakBindingInfoLinkEditAtom<A>::encode() -{ - // add regular atoms that override a dylib's weak definitions - for(std::set<const class ObjectFile::Atom*>::iterator it = fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->begin(); - it != fWriter.fRegularDefAtomsThatOverrideADylibsWeakDef->end(); ++it) { - if ( fWriter.shouldExport(**it) ) - fWriter.fWeakBindingInfo.push_back(BindingInfo(0, (*it)->getName(), true, 0, 0)); - } - - // add all exported weak definitions - for(std::vector<class ObjectFile::Atom*>::iterator it = fWriter.fAllAtoms->begin(); it != fWriter.fAllAtoms->end(); ++it) { - ObjectFile::Atom* atom = *it; - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition) && fWriter.shouldExport(*atom) ) { - fWriter.fWeakBindingInfo.push_back(BindingInfo(0, atom->getName(), false, 0, 0)); - } - } - - // sort by symbol, type, address - const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos; - std::vector<BindingInfo>& info = fWriter.fWeakBindingInfo; - if ( info.size() == 0 ) - return; - std::sort(info.begin(), info.end(), WeakBindingSorter()); - - // convert to temp encoding that can be more easily optimized - std::vector<binding_tmp> mid; - mid.reserve(info.size()); - const SegmentInfo* currentSegment = NULL; - unsigned int segIndex = 0; - const char* symbolName = NULL; - uint8_t type = 0; - uint64_t address = (uint64_t)(-1); - int64_t addend = 0; - for (std::vector<BindingInfo>::iterator it = info.begin(); it != info.end(); ++it) { - if ( symbolName != it->fSymbolName ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM, it->fFlags, 0, it->fSymbolName)); - symbolName = it->fSymbolName; - } - if ( it->fType != 0 ) { - if ( type != it->fType ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_TYPE_IMM, it->fType)); - type = it->fType; - } - if ( address != it->fAddress ) { - // non weak symbols just have BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM - // weak symbols have SET_SEG, ADD_ADDR, SET_ADDED, DO_BIND - if ( (currentSegment == NULL) || (it->fAddress < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <=it->fAddress) ) { - segIndex = 0; - for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= it->fAddress) && (it->fAddress < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - mid.push_back(binding_tmp(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, segIndex, it->fAddress - currentSegment->fBaseAddress)); - } - else { - mid.push_back(binding_tmp(BIND_OPCODE_ADD_ADDR_ULEB, it->fAddress-address)); - } - address = it->fAddress; - } - if ( addend != it->fAddend ) { - mid.push_back(binding_tmp(BIND_OPCODE_SET_ADDEND_SLEB, it->fAddend)); - addend = it->fAddend; - } - mid.push_back(binding_tmp(BIND_OPCODE_DO_BIND, 0)); - address += sizeof(pint_t); - } - } - mid.push_back(binding_tmp(BIND_OPCODE_DONE, 0)); - - - // optimize phase 1, combine bind/add pairs - binding_tmp* dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - if ( (src->opcode == BIND_OPCODE_DO_BIND) - && (src[1].opcode == BIND_OPCODE_ADD_ADDR_ULEB) ) { - dst->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB; - dst->operand1 = src[1].operand1; - ++src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 2, compress packed runs of BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB with - // same addr delta into one BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB - dst = &mid[0]; - for (const binding_tmp* src = &mid[0]; src->opcode != BIND_OPCODE_DONE; ++src) { - uint64_t delta = src->operand1; - if ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src[1].operand1 == delta) ) { - // found at least two in a row, this is worth compressing - dst->opcode = BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB; - dst->operand1 = 1; - dst->operand2 = delta; - ++src; - while ( (src->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (src->operand1 == delta) ) { - dst->operand1++; - ++src; - } - --src; - ++dst; - } - else { - *dst++ = *src; - } - } - dst->opcode = BIND_OPCODE_DONE; - - // optimize phase 3, use immediate encodings - for (binding_tmp* p = &mid[0]; p->opcode != REBASE_OPCODE_DONE; ++p) { - if ( (p->opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB) - && (p->operand1 < (15*sizeof(pint_t))) - && ((p->operand1 % sizeof(pint_t)) == 0) ) { - p->opcode = BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED; - p->operand1 = p->operand1/sizeof(pint_t); - } - } - dst->opcode = BIND_OPCODE_DONE; - - - // convert to compressed encoding - const static bool log = false; - fEncodedData.reserve(info.size()*2); - bool done = false; - for (std::vector<binding_tmp>::iterator it = mid.begin(); !done && it != mid.end() ; ++it) { - switch ( it->opcode ) { - case BIND_OPCODE_DONE: - if ( log ) fprintf(stderr, "BIND_OPCODE_DONE()\n"); - fEncodedData.append_byte(BIND_OPCODE_DONE); - done = true; - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (it->operand1 & BIND_IMMEDIATE_MASK)); - break; - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x%0llX, %s)\n", it->operand1, it->name); - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | it->operand1); - fEncodedData.append_string(it->name); - break; - case BIND_OPCODE_SET_TYPE_IMM: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_TYPE_IMM(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_TYPE_IMM | it->operand1); - break; - case BIND_OPCODE_SET_ADDEND_SLEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_ADDEND_SLEB(%lld)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); - fEncodedData.append_sleb128(it->operand1); - break; - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%lld, 0x%llX)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - case BIND_OPCODE_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND()\n"); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0x%llX)\n", it->operand1); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB); - fEncodedData.append_uleb128(it->operand1); - break; - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(%lld=0x%llX)\n", it->operand1, it->operand1*sizeof(pint_t)); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED | it->operand1 ); - break; - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: - if ( log ) fprintf(stderr, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(%lld, %lld)\n", it->operand1, it->operand2); - fEncodedData.append_byte(BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB); - fEncodedData.append_uleb128(it->operand1); - fEncodedData.append_uleb128(it->operand2); - break; - } - } - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - if (log) fprintf(stderr, "total weak binding info size = %ld\n", fEncodedData.size()); - -} - -template <typename A> -void CompressedLazyBindingInfoLinkEditAtom<A>::encode() -{ - // stream all lazy bindings and record start offsets - const SegmentInfo* currentSegment = NULL; - uint8_t segIndex = 0; - const std::vector<SegmentInfo*>& segments = fWriter.fSegmentInfos; - std::vector<class LazyPointerAtom<A>*>& allLazys = fWriter.fAllSynthesizedLazyPointers; - for (typename std::vector<class LazyPointerAtom<A>*>::iterator it = allLazys.begin(); it != allLazys.end(); ++it) { - LazyPointerAtom<A>* lazyPointerAtom = *it; - ObjectFile::Atom* lazyPointerTargetAtom = lazyPointerAtom->getTarget(); - - // skip lazy pointers that are bound non-lazily because they are coalesced - if ( ! fWriter.targetRequiresWeakBinding(*lazyPointerTargetAtom) ) { - // record start offset for use by stub helper - lazyPointerAtom->setLazyBindingInfoOffset(fEncodedData.size()); - - // write address to bind - pint_t address = lazyPointerAtom->getAddress(); - if ( (currentSegment == NULL) || (address < currentSegment->fBaseAddress) - || ((currentSegment->fBaseAddress+currentSegment->fSize) <= address) ) { - segIndex = 0; - for (std::vector<SegmentInfo*>::const_iterator segit = segments.begin(); segit != segments.end(); ++segit) { - if ( ((*segit)->fBaseAddress <= address) && (address < ((*segit)->fBaseAddress+(*segit)->fSize)) ) { - currentSegment = *segit; - break; - } - ++segIndex; - } - } - fEncodedData.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segIndex); - fEncodedData.append_uleb128(lazyPointerAtom->getAddress() - currentSegment->fBaseAddress); - - // write ordinal - int ordinal = fWriter.compressedOrdinalForImortedAtom(lazyPointerTargetAtom); - if ( ordinal <= 0 ) { - // special lookups are encoded as negative numbers in BindingInfo - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | (ordinal & BIND_IMMEDIATE_MASK) ); - } - else if ( ordinal <= 15 ) { - // small ordinals are encoded in opcode - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | ordinal); - } - else { - fEncodedData.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB); - fEncodedData.append_uleb128(ordinal); - } - // write symbol name - bool weak_import = fWriter.fWeakImportMap[lazyPointerTargetAtom]; - if ( weak_import ) - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | BIND_SYMBOL_FLAGS_WEAK_IMPORT); - else - fEncodedData.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); - fEncodedData.append_string(lazyPointerTargetAtom->getName()); - // write do bind - fEncodedData.append_byte(BIND_OPCODE_DO_BIND); - fEncodedData.append_byte(BIND_OPCODE_DONE); - } - } - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); - - //fprintf(stderr, "lazy binding info size = %ld, for %ld entries\n", fEncodedData.size(), allLazys.size()); -} - -struct TrieEntriesSorter -{ - TrieEntriesSorter(Options& o) : fOptions(o) {} - - bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) - { - unsigned int leftOrder; - unsigned int rightOrder; - fOptions.exportedSymbolOrder(left.name, &leftOrder); - fOptions.exportedSymbolOrder(right.name, &rightOrder); - if ( leftOrder != rightOrder ) - return (leftOrder < rightOrder); - else - return (left.address < right.address); - } -private: - Options& fOptions; -}; - - -template <typename A> -void CompressedExportInfoLinkEditAtom<A>::encode() -{ - // make vector of mach_o::trie::Entry for all exported symbols - std::vector<class ObjectFile::Atom*>& exports = fWriter.fExportedAtoms; - uint64_t imageBaseAddress = fWriter.fMachHeaderAtom->getAddress(); - std::vector<mach_o::trie::Entry> entries; - entries.reserve(exports.size()); - for (std::vector<ObjectFile::Atom*>::iterator it = exports.begin(); it != exports.end(); ++it) { - ObjectFile::Atom* atom = *it; - uint64_t flags = 0; - if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) - flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - uint64_t address = atom->getAddress() - imageBaseAddress; - if ( atom->isThumb() ) - address |= 1; - mach_o::trie::Entry entry; - entry.name = atom->getName(); - entry.flags = flags; - entry.address = address; - entries.push_back(entry); - } - - // sort vector by -exported_symbols_order, and any others by address - std::sort(entries.begin(), entries.end(), TrieEntriesSorter(fWriter.fOptions)); - - // create trie - mach_o::trie::makeTrie(entries, fEncodedData.bytes()); - - // align to pointer size - fEncodedData.pad_to_size(sizeof(pint_t)); -} - - - - - -}; // namespace executable -}; // namespace mach_o - - -#endif // __EXECUTABLE_MACH_O__ diff --git a/src/ld/ObjectFile.h b/src/ld/ObjectFile.h deleted file mode 100644 index 2710bfb..0000000 --- a/src/ld/ObjectFile.h +++ /dev/null @@ -1,385 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#ifndef __OBJECTFILE__ -#define __OBJECTFILE__ - -#include <stdint.h> -#include <vector> -#include <map> -#include <set> - - - -// -// These classes represent the abstract Atoms and References that are the basis of the linker. -// An Atom and a Reference correspond to a Node and Edge in graph theory. -// -// A Reader is a class which parses an object file and presents it as Atoms and References. -// All linking operations are done on Atoms and References. This makes the linker file -// format independent. -// -// A Writer takes a vector of Atoms with all References resolved and produces an executable file. -// -// - - - -namespace ObjectFile { - - -struct LineInfo -{ - uint32_t atomOffset; - const char* fileName; - uint32_t lineNumber; -}; - - -class ReaderOptions -{ -public: - ReaderOptions() : fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), - fLinkingMainExecutable(false), - fForFinalLinkedImage(false), fNoEHLabels(false), fForStatic(false), fForDyld(false), fMakeTentativeDefinitionsReal(false), - fWhyLoad(false), fRootSafe(false), fSetuidSafe(false),fDebugInfoStripping(kDebugInfoFull), - fImplicitlyLinkPublicDylibs(true), - fAddCompactUnwindEncoding(true), - fWarnCompactUnwind(false), - fRemoveDwarfUnwindIfCompactExists(false), - fMakeCompressedDyldInfo(false), - fAutoOrderInitializers(true), - fOptimizeZeroFill(true), - fLogObjectFiles(false), fLogAllFiles(false), - fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), - fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} - enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; - enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6, k10_7 }; - enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0, k3_1, k3_2, k4_0 }; - - struct AliasPair { - const char* realName; - const char* alias; - }; - - bool fFullyLoadArchives; - bool fLoadAllObjcObjectsFromArchives; - bool fFlatNamespace; - bool fLinkingMainExecutable; - bool fForFinalLinkedImage; - bool fNoEHLabels; - bool fForStatic; - bool fForDyld; - bool fMakeTentativeDefinitionsReal; - bool fWhyLoad; - bool fRootSafe; - bool fSetuidSafe; - DebugInfoStripping fDebugInfoStripping; - bool fImplicitlyLinkPublicDylibs; - bool fAddCompactUnwindEncoding; - bool fWarnCompactUnwind; - bool fRemoveDwarfUnwindIfCompactExists; - bool fMakeCompressedDyldInfo; - bool fAutoOrderInitializers; - bool fOptimizeZeroFill; - bool fLogObjectFiles; - bool fLogAllFiles; - bool fTraceDylibs; - bool fTraceIndirectDylibs; - bool fTraceArchives; - const char* fTraceOutputFile; - MacVersionMin fMacVersionMin; - IPhoneVersionMin fIPhoneVersionMin; - std::vector<AliasPair> fAliases; -}; - - -class Reader -{ -public: - enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; - struct Stab - { - class Atom* atom; - uint8_t type; - uint8_t other; - uint16_t desc; - uint32_t value; - const char* string; - }; - enum ObjcConstraint { kObjcNone, kObjcRetainRelease, kObjcRetainReleaseOrGC, kObjcGC }; - enum CpuConstraint { kCpuAny = 0 }; - - class DylibHander - { - public: - virtual ~DylibHander() {} - virtual Reader* findDylib(const char* installPath, const char* fromPath) = 0; - }; - - - static Reader* createReader(const char* path, const ReaderOptions& options); - - virtual const char* getPath() = 0; - virtual time_t getModificationTime() = 0; - virtual DebugInfoKind getDebugInfoKind() = 0; - virtual std::vector<class Atom*>& getAtoms() = 0; - virtual std::vector<class Atom*>* getJustInTimeAtomsFor(const char* name) = 0; - virtual std::vector<Stab>* getStabs() = 0; - virtual ObjcConstraint getObjCConstraint() { return kObjcNone; } - virtual uint32_t updateCpuConstraint(uint32_t current) { return current; } - virtual bool objcReplacementClasses() { return false; } - - // For relocatable object files only - virtual bool canScatterAtoms() { return true; } - virtual bool optimize(const std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&, - std::vector<const char*>&, const std::set<ObjectFile::Atom*>&, - std::vector<ObjectFile::Atom*>&, - uint32_t, ObjectFile::Reader* writer, - ObjectFile::Atom* entryPointAtom, - const std::vector<const char*>& llvmOptions, - bool allGlobalsAReDeadStripRoots, int okind, - bool verbose, bool saveTemps, const char* outputFilePath, - bool pie, bool allowTextRelocs) { return false; } - virtual bool hasLongBranchStubs() { return false; } - - // For Dynamic Libraries only - virtual const char* getInstallPath() { return NULL; } - virtual uint32_t getTimestamp() { return 0; } - virtual uint32_t getCurrentVersion() { return 0; } - virtual uint32_t getCompatibilityVersion() { return 0; } - virtual void processIndirectLibraries(DylibHander* handler) { } - virtual void setExplicitlyLinked() { } - virtual bool explicitlyLinked() { return false; } - virtual bool implicitlyLinked() { return false; } - virtual bool providedExportAtom() { return false; } - virtual const char* parentUmbrella() { return NULL; } - virtual std::vector<const char*>* getAllowableClients() { return NULL; } - virtual bool hasWeakExternals() { return false; } - virtual bool deadStrippable() { return false; } - virtual bool isLazyLoadedDylib() { return false; } - -protected: - Reader() {} - virtual ~Reader() {} -}; - -class Segment -{ -public: - virtual const char* getName() const = 0; - virtual bool isContentReadable() const = 0; - virtual bool isContentWritable() const = 0; - virtual bool isContentExecutable() const = 0; - - uint64_t getBaseAddress() const { return fBaseAddress; } - void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } - virtual bool hasFixedAddress() const { return false; } - -protected: - Segment() : fBaseAddress(0) {} - virtual ~Segment() {} - uint64_t fBaseAddress; -}; - -class Reference; - -class Section -{ -public: - unsigned int getIndex() { return fIndex; } - uint64_t getBaseAddress() { return fBaseAddress; } - void setBaseAddress(uint64_t addr) { fBaseAddress = addr; } - void* fOther; - -protected: - Section() : fOther(NULL), fBaseAddress(0), fIndex(0) {} - uint64_t fBaseAddress; - unsigned int fIndex; -}; - - -struct Alignment -{ - Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} - uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } - uint16_t powerOf2; - uint16_t modulus; -}; - -struct UnwindInfo -{ - uint32_t startOffset; - uint32_t unwindInfo; - - typedef UnwindInfo* iterator; - -}; - - -// -// An atom is the fundamental unit of linking. A C function or global variable is an atom. -// An atom has content and some attributes. The content of a function atom is the instructions -// that implement the function. The content of a global variable atom is its initial bits. -// -// Name: -// The name of an atom is the label name generated by the compiler. A C compiler names foo() -// as _foo. A C++ compiler names foo() as __Z3foov. -// The name refers to the first byte of the content. An atom cannot have multiple entry points. -// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. -// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. -// -// Scope: -// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond -// to the C visibility of static, hidden, default. -// -// DefinitionKind: -// An atom is one of five defintion kinds: -// regular Most atoms. -// weak C++ compiler makes some functions weak if there might be multiple copies -// that the linker needs to coalesce. -// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. -// It could be a prototype or it could be a definition. -// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists -// so that all References can be resolved. -// external-weak Same as external, but the definition in the dylib is weak. -// -// SymbolTableInclusion: -// An atom may or may not be in the symbol table in an object file. -// in Most atoms for functions or global data -// not-in Anonymous atoms such literal c-strings, or other compiler generated data -// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) -// -// Ordinal: -// When a reader is created it is given a base ordinal number. All atoms created by the reader -// should return a contiguous range of ordinal values that start at the base ordinal. The ordinal -// values are used by the linker to sort the atom graph when producing the output file. -// -class Atom -{ -public: - enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; - enum DefinitionKind { kRegularDefinition, kWeakDefinition, kTentativeDefinition, kExternalDefinition, kExternalWeakDefinition, kAbsoluteSymbol }; - enum ContentType { kUnclassifiedType, kCStringType, kCFIType, kLSDAType, kSectionStart, kSectionEnd, kBranchIsland, - kLazyPointer, kStub, kNonLazyPointer, kLazyDylibPointer, kStubHelper }; - enum SymbolTableInclusion { kSymbolTableNotIn, kSymbolTableIn, kSymbolTableInAndNeverStrip, kSymbolTableInAsAbsolute }; - - virtual Reader* getFile() const = 0; - virtual bool getTranslationUnitSource(const char** dir, const char** name) const = 0; - virtual const char* getName() const = 0; - virtual const char* getDisplayName() const = 0; - virtual Scope getScope() const = 0; - virtual DefinitionKind getDefinitionKind() const = 0; - virtual ContentType getContentType() const { return kUnclassifiedType; } - virtual SymbolTableInclusion getSymbolTableInclusion() const = 0; - virtual bool dontDeadStrip() const = 0; - virtual bool isZeroFill() const = 0; - virtual bool isThumb() const = 0; - virtual uint64_t getSize() const = 0; - virtual std::vector<ObjectFile::Reference*>& getReferences() const = 0; - virtual bool mustRemainInSection() const = 0; - virtual const char* getSectionName() const = 0; - virtual Segment& getSegment() const = 0; - virtual Atom& getFollowOnAtom() const = 0; - virtual uint32_t getOrdinal() const = 0; - virtual std::vector<LineInfo>* getLineInfo() const = 0; - virtual Alignment getAlignment() const = 0; - virtual void copyRawContent(uint8_t buffer[]) const = 0; - virtual void setScope(Scope) = 0; - virtual UnwindInfo::iterator beginUnwind() { return NULL; } - virtual UnwindInfo::iterator endUnwind() { return NULL; } - virtual Reference* getLSDA() { return NULL; } - virtual Reference* getFDE() { return NULL; } - virtual Atom* getPersonalityPointer() { return NULL; } - - uint64_t getSectionOffset() const { return fSectionOffset; } - uint64_t getAddress() const { return fSection->getBaseAddress() + fSectionOffset; } - class Section* getSection() const { return fSection; } - - virtual void setSectionOffset(uint64_t offset) { fSectionOffset = offset; } - virtual void setSection(class Section* sect) { fSection = sect; } - -protected: - Atom() : fSectionOffset(0), fSection(NULL) {} - virtual ~Atom() {} - - uint64_t fSectionOffset; - class Section* fSection; -}; - - -// -// A Reference is a directed edge to another Atom. When an instruction in -// the content of an Atom refers to another Atom, that is represented by a -// Reference. -// -// There are two kinds of references: direct and by-name. With a direct Reference, -// the target is bound by the Reader that created it. For instance a reference to a -// static would produce a direct reference. A by-name reference requires the linker -// to find the target Atom with the required name in order to be bound. -// -// For a link to succeed all References must be bound. -// -// A Reference has an optional "from" target. This is used when the content to fix-up -// is the difference of two Atom address. For instance, if a pointer sized data Atom -// is to contain A - B, then the Atom would have on Reference with a target of "A" and -// a from-target of "B". -// -// A Reference also has a fix-up-offset. This is the offset into the content of the -// Atom holding the reference where the fix-up (relocation) will be applied. -// -// -// -class Reference -{ -public: - enum TargetBinding { kUnboundByName, kBoundDirectly, kBoundByName, kDontBind }; - - virtual TargetBinding getTargetBinding() const = 0; - virtual TargetBinding getFromTargetBinding() const = 0; - virtual uint8_t getKind() const = 0; - virtual uint64_t getFixUpOffset() const = 0; - virtual const char* getTargetName() const = 0; - virtual Atom& getTarget() const = 0; - virtual uint64_t getTargetOffset() const = 0; - virtual Atom& getFromTarget() const = 0; - virtual const char* getFromTargetName() const = 0; - virtual uint64_t getFromTargetOffset() const = 0; - - virtual void setTarget(Atom&, uint64_t offset) = 0; - virtual void setFromTarget(Atom&) = 0; - virtual const char* getDescription() const = 0; - virtual bool isBranch() const { return false; } - -protected: - Reference() {} - virtual ~Reference() {} -}; - - -}; // namespace ObjectFile - - -#endif // __OBJECTFILE__ diff --git a/src/ld/OpaqueSection.hpp b/src/ld/OpaqueSection.hpp deleted file mode 100644 index cddd45c..0000000 --- a/src/ld/OpaqueSection.hpp +++ /dev/null @@ -1,199 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __OPAQUE_SECTION__ -#define __OPAQUE_SECTION__ - - -#include <vector> - -#include "ObjectFile.h" - -namespace opaque_section { - - -class Segment : public ObjectFile::Segment -{ -public: - Segment(const char* name) { fName = name; } - virtual const char* getName() const { return fName; } - virtual bool isContentReadable() const { return true; } - virtual bool isContentWritable() const { return false; } - virtual bool isContentExecutable() const { return (strcmp(fName, "__TEXT") == 0); } -private: - const char* fName; -}; - - -class Reader : public ObjectFile::Reader -{ -public: - Reader(const char* segmentName, const char* sectionName, const char* path, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName=NULL); - virtual ~Reader(); - - void addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, - uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom=NULL, uint64_t offsetInFromTarget=0); - - virtual const char* getPath() { return fPath; } - virtual time_t getModificationTime() { return 0; } - virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } - virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return fAtoms; } - virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; } - virtual std::vector<Stab>* getStabs() { return NULL; } - -private: - const char* fPath; - std::vector<class ObjectFile::Atom*> fAtoms; -}; - -class Reference : public ObjectFile::Reference -{ -public: - Reference(uint8_t kind, uint64_t fixupOffset, const ObjectFile::Atom* target, uint64_t targetOffset, - const ObjectFile::Atom* fromTarget=NULL, uint64_t fromTargetOffset=0) - : fFixUpOffset(fixupOffset), fTarget(target), fTargetOffset(targetOffset), fKind(kind), - fFromTarget(fromTarget), fFromTargetOffset(fromTargetOffset) {} - virtual ~Reference() {} - - - virtual ObjectFile::Reference::TargetBinding getTargetBinding() const { return ObjectFile::Reference::kBoundDirectly; } - virtual ObjectFile::Reference::TargetBinding getFromTargetBinding() const{ return ObjectFile::Reference::kDontBind; } - virtual uint8_t getKind() const { return fKind; } - virtual uint64_t getFixUpOffset() const { return fFixUpOffset; } - virtual const char* getTargetName() const { return fTarget->getName(); } - virtual ObjectFile::Atom& getTarget() const { return *((ObjectFile::Atom*)fTarget); } - virtual uint64_t getTargetOffset() const { return fTargetOffset; } - virtual ObjectFile::Atom& getFromTarget() const { return *((ObjectFile::Atom*)fFromTarget); } - virtual const char* getFromTargetName() const { return fFromTarget->getName(); } - virtual uint64_t getFromTargetOffset() const { return fFromTargetOffset; } - virtual void setTarget(ObjectFile::Atom&, uint64_t offset) { throw "can't set target"; } - virtual void setFromTarget(ObjectFile::Atom&) { throw "can't set from target"; } - virtual const char* getDescription() const { return "opaque section reference"; } - -private: - uint64_t fFixUpOffset; - const ObjectFile::Atom* fTarget; - uint64_t fTargetOffset; - uint8_t fKind; - const ObjectFile::Atom* fFromTarget; - uint64_t fFromTargetOffset; -}; - - -class Atom : public ObjectFile::Atom { -public: - virtual ObjectFile::Reader* getFile() const { return &fOwner; } - virtual bool getTranslationUnitSource(const char** dir, const char** name) const { return false; } - virtual const char* getName() const { return fName; } - virtual const char* getDisplayName() const; - virtual Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } - virtual DefinitionKind getDefinitionKind() const { return kRegularDefinition; } - virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } - virtual bool dontDeadStrip() const { return true; } - virtual bool isZeroFill() const { return false; } - virtual bool isThumb() const { return false; } - virtual uint64_t getSize() const { return fFileLength; } - virtual std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)(fReferences); } - virtual bool mustRemainInSection() const { return false; } - virtual const char* getSectionName() const { return fSectionName; } - virtual Segment& getSegment() const { return fSegment; } - virtual ObjectFile::Atom& getFollowOnAtom() const { return *((ObjectFile::Atom*)NULL); } - virtual uint32_t getOrdinal() const { return fOrdinal; } - virtual std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; } - virtual ObjectFile::Alignment getAlignment() const { return ObjectFile::Alignment(4); } - virtual void copyRawContent(uint8_t buffer[]) const; - - virtual void setScope(Scope) { } - -protected: - friend class Reader; - - Atom(Reader& owner, Segment& segment, const char* sectionName, - const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName); - virtual ~Atom() {} - - Reader& fOwner; - Segment& fSegment; - const char* fName; - const char* fSectionName; - const uint8_t* fFileContent; - uint32_t fOrdinal; - uint64_t fFileLength; - std::vector<ObjectFile::Reference*> fReferences; -}; - - - -Atom::Atom(Reader& owner, Segment& segment, const char* sectionName, const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, const char* symbolName) - : fOwner(owner), fSegment(segment), fSectionName(sectionName), fFileContent(fileContent), fOrdinal(ordinal), fFileLength(fileLength) -{ - if ( symbolName != NULL ) - fName = strdup(symbolName); - else - asprintf((char**)&fName, "__section$%s%s", segment.getName(), sectionName); -} - - -Reader::Reader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], - uint64_t fileLength, uint32_t ordinal, const char* symbolName) - : fPath(path) -{ - fAtoms.push_back(new Atom(*this, *(new Segment(segmentName)), strdup(sectionName), fileContent, fileLength, ordinal, symbolName)); -} - -Reader::~Reader() -{ -} - -void Reader::addSectionReference(uint8_t kind, uint64_t offsetInSection, const ObjectFile::Atom* targetAtom, - uint64_t offsetInTarget, const ObjectFile::Atom* fromTargetAtom, uint64_t offsetInFromTarget) -{ - fAtoms[0]->getReferences().push_back(new Reference(kind, offsetInSection, targetAtom, offsetInTarget, fromTargetAtom, offsetInFromTarget)); -} - - -const char* Atom::getDisplayName() const -{ - static char name[64]; - sprintf(name, "opaque section %s %s", fSegment.getName(), fSectionName); - return name; -} - - -void Atom::copyRawContent(uint8_t buffer[]) const -{ - memcpy(buffer, fFileContent, fFileLength); -} - - - -}; - - - -#endif // __OPAQUE_SECTION__ - - - diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 492d140..1de3379 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,6 +28,7 @@ #include <mach/vm_prot.h> #include <mach-o/dyld.h> #include <fcntl.h> + #include <vector> #include "configure.h" @@ -35,11 +36,15 @@ #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" -extern void printLTOVersion(Options &opts); +// upward dependency on lto::version() +namespace lto { + extern const char* version(); +} // magic to place command line in crash reports +const int crashreporterBufferSize = 2000; extern "C" char* __crashreporter_info__; -static char crashreporterBuffer[1000]; +static char crashreporterBuffer[crashreporterBufferSize]; char* __crashreporter_info__ = crashreporterBuffer; static bool sEmitWarnings = true; @@ -81,31 +86,54 @@ void throwf(const char* format, ...) } Options::Options(int argc, const char* argv[]) - : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fOutputKind(kDynamicExecutable), + : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false), - fInterposeMode(kInterposeNone), fDeadStrip(kDeadStripOff), fNameSpace(kTwoLevelNameSpace), - fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), fBaseAddress(0), + fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace), + fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"), + fBaseAddress(0), fMaxAddress(0x7FFFFFFFFFFFFFFFLL), fBaseWritableAddress(0), fSplitSegs(false), - fExportMode(kExportDefault), fLibrarySearchMode(kSearchAllDirsForDylibsThenAllDirsForArchives), - fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(false), + fExportMode(kExportDefault), fLibrarySearchMode(kSearchDylibAndArchiveInEachDir), + fUndefinedTreatment(kUndefinedError), fMessagesPrefixedWithArchitecture(true), fWeakReferenceMismatchTreatment(kWeakReferenceMismatchNonWeak), fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), + fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fExecutableStack(false), + fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), fSharedRegionEligible(false), fPrintOrderFileStatistics(false), - fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), + fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fUsingLazyDylibLinking(false), fEncryptable(true), fOrderData(true), fMarkDeadStrippableDylib(false), - fMakeClassicDyldInfo(true), fMakeCompressedDyldInfo(true), fAllowCpuSubtypeMismatches(false), - fUseSimplifiedDylibReExports(false), fObjCABIVersion2POverride(false), fSaveTempFiles(false) + fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), + fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false), + fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false), + fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), + fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false), + fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false), + fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true), + fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), + fAutoOrderInitializers(true), fOptimizeZeroFill(true), fLogObjectFiles(false), + fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), + fOutputSlidable(false), fWarnWeakExports(false), + fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), + fDemangle(false), fTLVSupport(false), +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + fVersionLoadCommand(false), fFunctionStartsLoadCommand(false), +#else + fVersionLoadCommand(true), fFunctionStartsLoadCommand(true), +#endif + fCanReExportSymbols(false), fObjcCategoryMerging(true), + fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), + fMacVersionMin(ld::macVersionUnset), fIPhoneVersionMin(ld::iPhoneVersionUnset), + fSaveTempFiles(false) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -119,48 +147,10 @@ Options::~Options() { } -const ObjectFile::ReaderOptions& Options::readerOptions() -{ - return fReaderOptions; -} - - -const char* Options::getOutputFilePath() -{ - return fOutputFile; -} - -std::vector<Options::FileInfo>& Options::getInputFiles() -{ - return fInputFiles; -} - -Options::OutputKind Options::outputKind() -{ - return fOutputKind; -} -bool Options::bindAtLoad() -{ - return fBindAtLoad; -} -bool Options::prebind() -{ - return fPrebind; -} -bool Options::fullyLoadArchives() -{ - return fReaderOptions.fFullyLoadArchives; -} - -Options::NameSpace Options::nameSpace() -{ - return fNameSpace; -} - -const char* Options::installPath() +const char* Options::installPath() const { if ( fDylibInstallName != NULL ) return fDylibInstallName; @@ -170,32 +160,8 @@ const char* Options::installPath() return fOutputFile; } -uint32_t Options::currentVersion() -{ - return fDylibCurrentVersion; -} - -uint32_t Options::compatibilityVersion() -{ - return fDylibCompatVersion; -} - -const char* Options::entryName() -{ - return fEntryName; -} - -uint64_t Options::baseAddress() -{ - return fBaseAddress; -} - -bool Options::keepPrivateExterns() -{ - return fKeepPrivateExterns; -} -bool Options::interposable(const char* name) +bool Options::interposable(const char* name) const { switch ( fInterposeMode ) { case kInterposeNone: @@ -208,121 +174,34 @@ bool Options::interposable(const char* name) throw "internal error"; } -bool Options::needsModuleTable() -{ - return fNeedsModuleTable; -} - -bool Options::ignoreOtherArchInputFiles() -{ - return fIgnoreOtherArchFiles; -} - -bool Options::forceCpuSubtypeAll() -{ - return fForceSubtypeAll; -} - -bool Options::traceDylibs() -{ - return fReaderOptions.fTraceDylibs; -} - -bool Options::traceArchives() -{ - return fReaderOptions.fTraceArchives; -} - -Options::UndefinedTreatment Options::undefinedTreatment() -{ - return fUndefinedTreatment; -} - -Options::WeakReferenceMismatchTreatment Options::weakReferenceMismatchTreatment() -{ - return fWeakReferenceMismatchTreatment; -} - -const char* Options::umbrellaName() -{ - return fUmbrellaName; -} - -std::vector<const char*>& Options::allowableClients() -{ - return fAllowableClients; -} - -const char* Options::clientName() -{ - return fClientName; -} - -uint64_t Options::zeroPageSize() -{ - return fZeroPageSize; -} - -bool Options::hasCustomStack() -{ - return (fStackSize != 0); -} - -uint64_t Options::customStackSize() -{ - return fStackSize; -} - -uint64_t Options::customStackAddr() -{ - return fStackAddr; -} -bool Options::hasExecutableStack() -{ - return fExecutableStack; -} -std::vector<const char*>& Options::initialUndefines() -{ - return fInitialUndefines; -} -bool Options::printWhyLive(const char* symbolName) +bool Options::printWhyLive(const char* symbolName) const { return ( fWhyLive.find(symbolName) != fWhyLive.end() ); } -const char* Options::initFunctionName() -{ - return fInitFunctionName; -} - const char* Options::dotOutputFile() { return fDotOutputFile; } -bool Options::hasExportRestrictList() -{ - return (fExportMode != kExportDefault); -} -bool Options::hasExportMaskList() +bool Options::hasWildCardExportRestrictList() const { - return (fExportMode == kExportSome); + // has -exported_symbols_list which contains some wildcards + return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); } - -bool Options::hasWildCardExportRestrictList() +bool Options::hasWeakBitTweaks() const { // has -exported_symbols_list which contains some wildcards - return ((fExportMode == kExportSome) && fExportSymbols.hasWildCards()); + return (!fForceWeakSymbols.empty() || !fForceNotWeakSymbols.empty()); } - -bool Options::allGlobalsAreDeadStripRoots() +bool Options::allGlobalsAreDeadStripRoots() const { // -exported_symbols_list means globals are not exported by default if ( fExportMode == kExportSome ) @@ -344,59 +223,122 @@ bool Options::allGlobalsAreDeadStripRoots() return false; } -uint32_t Options::minimumHeaderPad() + +bool Options::keepRelocations() { - return fMinimumHeaderPad; + return fKeepRelocations; } -std::vector<Options::ExtraSection>& Options::extraSections() +bool Options::warnStabs() { - return fExtraSections; + return fWarnStabs; } -std::vector<Options::SectionAlignment>& Options::sectionAlignments() +const char* Options::executablePath() { - return fSectionAlignments; + return fExecutablePath; } -Options::CommonsMode Options::commonsMode() + +uint32_t Options::initialSegProtection(const char* segName) const { - return fCommonsMode; + for(std::vector<Options::SegmentProtect>::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) { + return it->init; + } + } + if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } + else if ( strcmp(segName, "__TEXT") == 0 ) { + return VM_PROT_READ | VM_PROT_EXECUTE; + } + else if ( strcmp(segName, "__LINKEDIT") == 0 ) { + return VM_PROT_READ; + } + + // all others default to read-write + return VM_PROT_READ | VM_PROT_WRITE; } -bool Options::warnCommons() +uint32_t Options::maxSegProtection(const char* segName) const { - return fWarnCommons; + // iPhoneOS always uses same protection for max and initial + if ( fIPhoneVersionMin != ld::iPhoneVersionUnset ) + return initialSegProtection(segName); + + for(std::vector<Options::SegmentProtect>::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) { + return it->max; + } + } + if ( strcmp(segName, "__PAGEZERO") == 0 ) { + return 0; + } + // all others default to all + return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; +} + +uint64_t Options::segPageSize(const char* segName) const +{ + for(std::vector<SegmentSize>::const_iterator it=fCustomSegmentSizes.begin(); it != fCustomSegmentSizes.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return it->size; + } + return fSegmentAlignment; } -bool Options::keepRelocations() +uint64_t Options::customSegmentAddress(const char* segName) const { - return fKeepRelocations; + for(std::vector<SegmentStart>::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return it->address; + } + // if custom stack in use, model as segment with custom address + if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) + return fStackAddr - fStackSize; + return 0; } -bool Options::warnStabs() +bool Options::hasCustomSegmentAddress(const char* segName) const { - return fWarnStabs; + for(std::vector<SegmentStart>::const_iterator it=fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { + if ( strcmp(it->name, segName) == 0 ) + return true; + } + // if custom stack in use, model as segment with custom address + if ( (fStackSize != 0) && (strcmp("__UNIXSTACK", segName) == 0) ) + return true; + return false; } -const char* Options::executablePath() +bool Options::hasCustomSectionAlignment(const char* segName, const char* sectName) const { - return fExecutablePath; + for (std::vector<SectionAlignment>::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { + if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) + return true; + } + return false; } -Options::DeadStripMode Options::deadStrip() +uint8_t Options::customSectionAlignment(const char* segName, const char* sectName) const { - return fDeadStrip; + for (std::vector<SectionAlignment>::const_iterator it = fSectionAlignments.begin(); it != fSectionAlignments.end(); ++it) { + if ( (strcmp(it->segmentName, segName) == 0) && (strcmp(it->sectionName, sectName) == 0) ) + return it->alignment; + } + return 0; } + bool Options::hasExportedSymbolOrder() { return (fExportSymbolsOrder.size() > 0); } -bool Options::exportedSymbolOrder(const char* sym, unsigned int* order) +bool Options::exportedSymbolOrder(const char* sym, unsigned int* order) const { - NameToOrder::iterator pos = fExportSymbolsOrder.find(sym); + NameToOrder::const_iterator pos = fExportSymbolsOrder.find(sym); if ( pos != fExportSymbolsOrder.end() ) { *order = pos->second; return true; @@ -478,8 +420,28 @@ void Options::loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderM // Note: we do not free() the malloc buffer, because the strings are used by the export-set hash table } +bool Options::forceWeak(const char* symbolName) const +{ + return fForceWeakSymbols.contains(symbolName); +} + +bool Options::forceNotWeak(const char* symbolName) const +{ + return fForceNotWeakSymbols.contains(symbolName); +} + +bool Options::forceWeakNonWildCard(const char* symbolName) const +{ + return fForceWeakSymbols.containsNonWildcard(symbolName); +} + +bool Options::forceNotWeakNonWildcard(const char* symbolName) const +{ + return fForceNotWeakSymbols.containsNonWildcard(symbolName); +} + -bool Options::shouldExport(const char* symbolName) +bool Options::shouldExport(const char* symbolName) const { switch (fExportMode) { case kExportSome: @@ -492,7 +454,12 @@ bool Options::shouldExport(const char* symbolName) throw "internal error"; } -bool Options::keepLocalSymbol(const char* symbolName) +bool Options::shouldReExport(const char* symbolName) const +{ + return fReExportSymbols.contains(symbolName); +} + +bool Options::keepLocalSymbol(const char* symbolName) const { switch (fLocalSymbolHandling) { case kLocalSymbolsAll: @@ -507,81 +474,204 @@ bool Options::keepLocalSymbol(const char* symbolName) throw "internal error"; } -void Options::parseArch(const char* architecture) + +void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) { - if ( architecture == NULL ) + fArchitecture = type; + fSubArchitecture = subtype; + switch ( type ) { + case CPU_TYPE_POWERPC: + switch ( subtype ) { + case CPU_SUBTYPE_POWERPC_750: + fArchitectureName = "ppc750"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_7400: + fArchitectureName = "ppc7400"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_7450: + fArchitectureName = "ppc7450"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_970: + fArchitectureName = "ppc970"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_POWERPC_ALL: + fArchitectureName = "ppc"; + fHasPreferredSubType = false; + break; + default: + assert(0 && "unknown ppc subtype"); + fArchitectureName = "ppc"; + break; + } + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + break; + case CPU_TYPE_POWERPC64: + fArchitectureName = "ppc64"; + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + warning("-macosx_version_min not specificed, assuming 10.5"); + fMacVersionMin = ld::mac10_5; + } + break; + case CPU_TYPE_I386: + fArchitectureName = "i386"; + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; + case CPU_TYPE_X86_64: + fArchitectureName = "x86_64"; + if ( (fMacVersionMin == ld::macVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + if ( !fMakeCompressedDyldInfo && (fMacVersionMin >= ld::mac10_6) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; + case CPU_TYPE_ARM: + switch ( subtype ) { + case CPU_SUBTYPE_ARM_V4T: + fArchitectureName = "armv4t"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_ARM_V5TEJ: + fArchitectureName = "armv5"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_ARM_V6: + fArchitectureName = "armv6"; + fHasPreferredSubType = true; + break; + case CPU_SUBTYPE_ARM_V7: + fArchitectureName = "armv7"; + fHasPreferredSubType = true; + break; + default: + assert(0 && "unknown arm subtype"); + fArchitectureName = "arm"; + break; + } + if ( (fMacVersionMin == ld::macVersionUnset) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) && (fOutputKind != Options::kObjectFile) ) { +#if defined(DEFAULT_IPHONEOS_MIN_VERSION) + warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + setIPhoneVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); +#elif defined(DEFAULT_MACOSX_MIN_VERSION) + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); +#else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; +#endif + } + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iPhone3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + break; + default: + fArchitectureName = "unknown architecture"; + break; + } +} + +void Options::parseArch(const char* arch) +{ + if ( arch == NULL ) throw "-arch must be followed by an architecture string"; - if ( strcmp(architecture, "ppc") == 0 ) { + fArchitectureName = arch; + if ( strcmp(arch, "ppc") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; } - else if ( strcmp(architecture, "ppc64") == 0 ) { + else if ( strcmp(arch, "ppc64") == 0 ) { fArchitecture = CPU_TYPE_POWERPC64; fSubArchitecture = CPU_SUBTYPE_POWERPC_ALL; } - else if ( strcmp(architecture, "i386") == 0 ) { + else if ( strcmp(arch, "i386") == 0 ) { fArchitecture = CPU_TYPE_I386; fSubArchitecture = CPU_SUBTYPE_I386_ALL; } - else if ( strcmp(architecture, "x86_64") == 0 ) { + else if ( strcmp(arch, "x86_64") == 0 ) { fArchitecture = CPU_TYPE_X86_64; fSubArchitecture = CPU_SUBTYPE_X86_64_ALL; } - else if ( strcmp(architecture, "arm") == 0 ) { + else if ( strcmp(arch, "arm") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_ALL; } // compatibility support for cpu-sub-types - else if ( strcmp(architecture, "ppc750") == 0 ) { + else if ( strcmp(arch, "ppc750") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_750; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "ppc7400") == 0 ) { + else if ( strcmp(arch, "ppc7400") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_7400; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "ppc7450") == 0 ) { + else if ( strcmp(arch, "ppc7450") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_7450; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "ppc970") == 0 ) { + else if ( strcmp(arch, "ppc970") == 0 ) { fArchitecture = CPU_TYPE_POWERPC; fSubArchitecture = CPU_SUBTYPE_POWERPC_970; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv6") == 0 ) { + else if ( strcmp(arch, "armv6") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V6; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv5") == 0 ) { + else if ( strcmp(arch, "armv5") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv4t") == 0 ) { + else if ( strcmp(arch, "armv4t") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V4T; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "xscale") == 0 ) { + else if ( strcmp(arch, "xscale") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE; fHasPreferredSubType = true; } - else if ( strcmp(architecture, "armv7") == 0 ) { + else if ( strcmp(arch, "armv7") == 0 ) { fArchitecture = CPU_TYPE_ARM; fSubArchitecture = CPU_SUBTYPE_ARM_V7; fHasPreferredSubType = true; } else - throwf("unknown/unsupported architecture name for: -arch %s", architecture); + throwf("unknown/unsupported architecture name for: -arch %s", arch); } -bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) +bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const { struct stat statBuffer; char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; @@ -723,7 +813,7 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi throwf("framework not found %s", rootName); } -Options::FileInfo Options::findFile(const char* path) +Options::FileInfo Options::findFile(const char* path) const { FileInfo result; struct stat statBuffer; @@ -731,7 +821,7 @@ Options::FileInfo Options::findFile(const char* path) // if absolute path and not a .o file, the use SDK prefix if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { const int pathLen = strlen(path); - for (std::vector<const char*>::iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { + for (std::vector<const char*>::const_iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { // ??? Shouldn't we be using String here? const char* sdkPathDir = *it; const int sdkPathDirLen = strlen(sdkPathDir); @@ -777,7 +867,7 @@ Options::FileInfo Options::findFile(const char* path) throwf("file not found: %s", path); } -Options::FileInfo Options::findFileUsingPaths(const char* path) +Options::FileInfo Options::findFileUsingPaths(const char* path) const { FileInfo result; @@ -803,7 +893,7 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { - for (std::vector<const char*>::iterator it = fFrameworkSearchPaths.begin(); + for (std::vector<const char*>::const_iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { const char* dir = *it; @@ -826,7 +916,7 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) && (strcmp(&leafName[leafLen-6], ".dylib") == 0) && (strstr(path, ".framework/") != NULL) ); if ( !embeddedDylib ) { - for (std::vector<const char*>::iterator it = fLibrarySearchPaths.begin(); + for (std::vector<const char*>::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -842,7 +932,7 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) } -void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath) +void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) { FILE* file = fopen(segAddrPath, "r"); if ( file == NULL ) { @@ -879,7 +969,7 @@ void Options::parseSegAddrTable(const char* segAddrPath, const char* installPath for(char* end = eol-1; (end > p) && isspace(*end); --end) *end = '\0'; // see if this line is for the dylib being linked - if ( strcmp(p, installPath) == 0 ) { + if ( strcmp(p, installPth) == 0 ) { fBaseAddress = firstColumAddress; if ( hasSecondColumn ) { fBaseWritableAddress = secondColumAddress; @@ -939,7 +1029,18 @@ void Options::loadFileList(const char* fileOfPaths) fclose(file); } -bool Options::SetWithWildcards::hasWildCards(const char* symbol) + +void Options::SetWithWildcards::remove(const NameSet& toBeRemoved) +{ + for(NameSet::const_iterator it=toBeRemoved.begin(); it != toBeRemoved.end(); ++it) { + const char* symbolName = *it; + NameSet::iterator pos = fRegular.find(symbolName); + if ( pos != fRegular.end() ) + fRegular.erase(pos); + } +} + +bool Options::SetWithWildcards::hasWildCards(const char* symbol) { // an exported symbol name containing *, ?, or [ requires wildcard matching return ( strpbrk(symbol, "*?[") != NULL ); @@ -953,21 +1054,28 @@ void Options::SetWithWildcards::insert(const char* symbol) fRegular.insert(symbol); } -bool Options::SetWithWildcards::contains(const char* symbol) +bool Options::SetWithWildcards::contains(const char* symbol) const { // first look at hash table on non-wildcard symbols if ( fRegular.find(symbol) != fRegular.end() ) return true; // next walk list of wild card symbols looking for a match - for(std::vector<const char*>::iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { + for(std::vector<const char*>::const_iterator it = fWildCard.begin(); it != fWildCard.end(); ++it) { if ( wildCardMatch(*it, symbol) ) return true; } return false; } +bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const +{ + // look at hash table on non-wildcard symbols + return ( fRegular.find(symbol) != fRegular.end() ); +} + + -bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) +bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const { ++p; // find end const char* b = p; @@ -996,7 +1104,7 @@ bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) return false; } -bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) +bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* symbol) const { const char* s = symbol; for (const char* p = pattern; *p != '\0'; ++p) { @@ -1031,6 +1139,8 @@ bool Options::SetWithWildcards::wildCardMatch(const char* pattern, const char* s void Options::loadExportFile(const char* fileOfExports, const char* option, SetWithWildcards& set) { + if ( fileOfExports == NULL ) + throwf("missing file after %s", option); // read in whole file int fd = ::open(fileOfExports, O_RDONLY, 0); if ( fd == -1 ) @@ -1117,7 +1227,7 @@ void Options::parseAliasFile(const char* fileOfAliases) ::close(fd); // parse into symbols and add to fAliases - ObjectFile::ReaderOptions::AliasPair pair; + AliasPair pair; char * const end = &p[stat_buf.st_size+1]; enum { lineStart, inRealName, inBetween, inAliasName, inComment } state = lineStart; int lineNumber = 1; @@ -1163,7 +1273,7 @@ void Options::parseAliasFile(const char* fileOfAliases) *last = '\0'; --last; } - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); state = inComment; } else if ( *s == '\n' ) { @@ -1174,7 +1284,7 @@ void Options::parseAliasFile(const char* fileOfAliases) *last = '\0'; --last; } - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); state = lineStart; } break; @@ -1228,34 +1338,8 @@ void Options::setMacOSXVersionMin(const char* version) throw "-macosx_version_min argument missing"; if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - int num = version[3] - '0'; - switch ( num ) { - case 0: - case 1: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; - break; - case 7: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_7; - break; - default: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_7; - break; - } + unsigned int minorVersion = version[3] - '0'; + fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); } else { warning("unknown option to -macosx_version_min, not 10.x"); @@ -1265,41 +1349,26 @@ void Options::setMacOSXVersionMin(const char* version) void Options::setIPhoneVersionMin(const char* version) { if ( version == NULL ) - throw "-iphoneos_version_min argument missing"; + throw "-ios_version_min argument missing"; if ( ! isdigit(version[0]) ) - throw "-iphoneos_version_min argument is not a number"; + throw "-ios_version_min argument is not a number"; if ( version[1] != '.' ) - throw "-iphoneos_version_min argument is missing period as second character"; + throw "-ios_version_min argument is missing period as second character"; if ( ! isdigit(version[2]) ) - throw "-iphoneos_version_min argument is not a number"; - - if ( (version[0] == '2') && (version[2] == '0') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - else if ( (version[0] == '2') && (version[2] == '1') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_1; - else if ( (version[0] == '2') && (version[2] >= '2') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; - else if ( (version[0] == '3') && (version[2] == '0') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; - else if ( (version[0] == '3') && (version[2] == '1') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; - else if ( (version[0] == '3') && (version[2] >= '2') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_2; - else if ( (version[0] >= '4') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k4_0; - else { - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - warning("unknown option to -iphoneos_version_min, not 2.x, 3.x, or 4.x"); - } + throw "-ios_version_min argument is not a number"; + + unsigned int majorVersion = version[0] - '0'; + unsigned int minorVersion = version[2] - '0'; + fIPhoneVersionMin = (ld::IPhoneVersionMin)((majorVersion << 16) | (minorVersion << 8)); } -bool Options::minOS(ObjectFile::ReaderOptions::MacVersionMin requiredMacMin, ObjectFile::ReaderOptions::IPhoneVersionMin requirediPhoneOSMin) +bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IPhoneVersionMin requirediPhoneOSMin) { - if ( fReaderOptions.fMacVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) { - return ( fReaderOptions.fMacVersionMin >= requiredMacMin ); + if ( fMacVersionMin != ld::macVersionUnset ) { + return ( fMacVersionMin >= requiredMacMin ); } else { - return ( fReaderOptions.fIPhoneVersionMin >= requirediPhoneOSMin); + return ( fIPhoneVersionMin >= requirediPhoneOSMin); } } @@ -1488,7 +1557,7 @@ static const char* cstringSymbolName(const char* orderFileString) void Options::parseOrderFile(const char* path, bool cstring) { // order files override auto-ordering - fReaderOptions.fAutoOrderInitializers = false; + fAutoOrderInitializers = false; // read in whole file int fd = ::open(path, O_RDONLY, 0); @@ -1721,6 +1790,8 @@ void Options::parse(int argc, const char* argv[]) fprintf (stderr, "[Logging ld64 options]\t%s\n", arg); if ( (arg[1] == 'L') || (arg[1] == 'F') ) { + if (arg[2] == '\0') + ++i; // previously handled by buildSearchPaths() } // The one gnu style option we have to keep compatibility @@ -1737,8 +1808,8 @@ void Options::parse(int argc, const char* argv[]) // default } else if ( strcmp(arg, "-static") == 0 ) { - fReaderOptions.fForStatic = true; - if ( fOutputKind != kObjectFile ) { + fForStatic = true; + if ( (fOutputKind != kObjectFile) && (fOutputKind != kKextBundle) ) { fOutputKind = kStaticExecutable; } } @@ -1767,6 +1838,12 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-o") == 0 ) { fOutputFile = argv[++i]; } + else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { + FileInfo info = findLibrary(&arg[7], true); + info.options.fLazyLoad = true; + addLibrary(info); + fUsingLazyDylibLinking = true; + } else if ( (arg[1] == 'l') && (strncmp(arg,"-lazy_",6) !=0) ) { addLibrary(findLibrary(&arg[2])); } @@ -1777,14 +1854,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fWeakImport = true; addLibrary(info); } - else if ( strncmp(arg, "-lazy-l", 7) == 0 ) { - FileInfo info = findLibrary(&arg[7], true); - info.options.fLazyLoad = true; - addLibrary(info); - fUsingLazyDylibLinking = true; - } // Avoid lazy binding. - // ??? Deprecate. else if ( strcmp(arg, "-bind_at_load") == 0 ) { fBindAtLoad = true; } @@ -1802,14 +1872,14 @@ void Options::parse(int argc, const char* argv[]) } // Similar to --whole-archive. else if ( strcmp(arg, "-all_load") == 0 ) { - fReaderOptions.fFullyLoadArchives = true; + fFullyLoadArchives = true; } else if ( strcmp(arg, "-noall_load") == 0) { warnObsolete(arg); } // Similar to -all_load else if ( strcmp(arg, "-ObjC") == 0 ) { - fReaderOptions.fLoadAllObjcObjectsFromArchives = true; + fLoadAllObjcObjectsFromArchives = true; } // Similar to -all_load, but for the following archive only. else if ( strcmp(arg, "-force_load") == 0 ) { @@ -1981,6 +2051,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-search_paths_first") == 0 ) { // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-search_dylibs_first") == 0 ) { + // previously handled by buildSearchPaths() + } else if ( strcmp(arg, "-undefined") == 0 ) { setUndefinedTreatment(argv[++i]); } @@ -2148,6 +2221,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; } + else if ( strcmp(arg, "-allow_heap_execute") == 0 ) { + fDisableNonExecutableHeap = true; + } else if ( strcmp(arg, "-sectalign") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectalign missing <segment> <section> <file-path>"; @@ -2179,7 +2255,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-macosx_version_min") == 0 ) { setMacOSXVersionMin(argv[++i]); } - else if ( strcmp(arg, "-iphoneos_version_min") == 0 ) { + else if ( (strcmp(arg, "-iphoneos_version_min") == 0) || (strcmp(arg, "-ios_version_min") == 0) ) { setIPhoneVersionMin(argv[++i]); } else if ( strcmp(arg, "-multiply_defined") == 0 ) { @@ -2209,7 +2285,7 @@ void Options::parse(int argc, const char* argv[]) warnObsolete(arg); } else if ( (strcmp(arg, "-why_load") == 0) || (strcmp(arg, "-whyload") == 0) ) { - fReaderOptions.fWhyLoad = true; + fWhyLoad = true; } else if ( strcmp(arg, "-why_live") == 0 ) { const char* name = argv[++i]; @@ -2232,36 +2308,36 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-s") == 0 ) { warnObsolete(arg); fLocalSymbolHandling = kLocalSymbolsNone; - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + fDebugInfoStripping = Options::kDebugInfoNone; } else if ( strcmp(arg, "-x") == 0 ) { fLocalSymbolHandling = kLocalSymbolsNone; } else if ( strcmp(arg, "-S") == 0 ) { - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + fDebugInfoStripping = Options::kDebugInfoNone; } else if ( strcmp(arg, "-X") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-Si") == 0 ) { warnObsolete(arg); - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + fDebugInfoStripping = Options::kDebugInfoFull; } else if ( strcmp(arg, "-b") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-Sn") == 0 ) { warnObsolete(arg); - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoFull; + fDebugInfoStripping = Options::kDebugInfoFull; } else if ( strcmp(arg, "-Sp") == 0 ) { warnObsolete(arg); } else if ( strcmp(arg, "-dead_strip") == 0 ) { - fDeadStrip = kDeadStripOnPlusUnusedInits; + fDeadStrip = true; } else if ( strcmp(arg, "-no_dead_strip_inits_and_terms") == 0 ) { - fDeadStrip = kDeadStripOn; + fDeadStrip = true; } else if ( strcmp(arg, "-w") == 0 ) { // previously handled by buildSearchPaths() @@ -2282,10 +2358,10 @@ void Options::parse(int argc, const char* argv[]) fMaxMinimumHeaderPad = true; } else if ( strcmp(arg, "-t") == 0 ) { - fReaderOptions.fLogAllFiles = true; + fLogAllFiles = true; } else if ( strcmp(arg, "-whatsloaded") == 0 ) { - fReaderOptions.fLogObjectFiles = true; + fLogObjectFiles = true; } else if ( strcmp(arg, "-A") == 0 ) { warnObsolete(arg); @@ -2356,7 +2432,7 @@ void Options::parse(int argc, const char* argv[]) fStatistics = true; } else if ( strcmp(arg, "-d") == 0 ) { - fReaderOptions.fMakeTentativeDefinitionsReal = true; + fMakeTentativeDefinitionsReal = true; } else if ( strcmp(arg, "-v") == 0 ) { // previously handled by buildSearchPaths() @@ -2381,20 +2457,20 @@ void Options::parse(int argc, const char* argv[]) fDtraceScriptName = name; } else if ( strcmp(arg, "-root_safe") == 0 ) { - fReaderOptions.fRootSafe = true; + fRootSafe = true; } else if ( strcmp(arg, "-setuid_safe") == 0 ) { - fReaderOptions.fSetuidSafe = true; + fSetuidSafe = true; } else if ( strcmp(arg, "-alias") == 0 ) { - ObjectFile::ReaderOptions::AliasPair pair; + Options::AliasPair pair; pair.realName = argv[++i]; if ( pair.realName == NULL ) throw "missing argument to -alias"; pair.alias = argv[++i]; if ( pair.alias == NULL ) throw "missing argument to -alias"; - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); } else if ( strcmp(arg, "-alias_list") == 0 ) { parseAliasFile(argv[++i]); @@ -2404,12 +2480,12 @@ void Options::parse(int argc, const char* argv[]) const char* colon = strchr(arg, ':'); if ( colon == NULL ) throwf("unknown option: %s", arg); - ObjectFile::ReaderOptions::AliasPair pair; + Options::AliasPair pair; char* temp = new char[colon-arg]; strlcpy(temp, &arg[2], colon-arg-1); pair.realName = &colon[1]; pair.alias = temp; - fReaderOptions.fAliases.push_back(pair); + fAliases.push_back(pair); } else if ( strcmp(arg, "-save-temps") == 0 ) { fSaveTempFiles = true; @@ -2433,6 +2509,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-pie") == 0 ) { fPositionIndependentExecutable = true; + fPIEOnCommandLine = true; } else if ( strcmp(arg, "-no_pie") == 0 ) { fDisablePositionIndependentExecutable = true; @@ -2452,11 +2529,26 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; addLibrary(info); } + else if ( strncmp(arg, "-upward-l", 9) == 0 ) { + FileInfo info = findLibrary(&arg[9], true); + info.options.fUpward = true; + addLibrary(info); + } + else if ( strcmp(arg, "-upward_library") == 0 ) { + FileInfo info = findFile(argv[++i]); + info.options.fUpward = true; + addLibrary(info); + } + else if ( strcmp(arg, "-upward_framework") == 0 ) { + FileInfo info = findFramework(argv[++i]); + info.options.fUpward = true; + addLibrary(info); + } else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { fDeadStripDylibs = true; } else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { - fReaderOptions.fImplicitlyLinkPublicDylibs = false; + fImplicitlyLinkPublicDylibs = false; } else if ( strcmp(arg, "-new_linker") == 0 ) { // ignore @@ -2465,7 +2557,7 @@ void Options::parse(int argc, const char* argv[]) fEncryptable = false; } else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { - fReaderOptions.fAddCompactUnwindEncoding = false; + fAddCompactUnwindEncoding = false; } else if ( strcmp(arg, "-mllvm") == 0 ) { const char* opts = argv[++i]; @@ -2474,7 +2566,7 @@ void Options::parse(int argc, const char* argv[]) fLLVMOptions.push_back(opts); } else if ( strcmp(arg, "-no_order_inits") == 0 ) { - fReaderOptions.fAutoOrderInitializers = false; + fAutoOrderInitializers = false; } else if ( strcmp(arg, "-no_order_data") == 0 ) { fOrderData = false; @@ -2498,30 +2590,108 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { fMakeCompressedDyldInfo = false; + fMakeCompressedDyldInfoForceOff = true; } else if ( strcmp(arg, "-no_eh_labels") == 0 ) { - fReaderOptions.fNoEHLabels = true; + fNoEHLabels = true; } else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) { - fReaderOptions.fWarnCompactUnwind = true; + fWarnCompactUnwind = true; } else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { fAllowCpuSubtypeMismatches = true; } else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { - fReaderOptions.fOptimizeZeroFill = false; + fOptimizeZeroFill = false; } else if ( strcmp(arg, "-objc_abi_version") == 0 ) { const char* version = argv[++i]; if ( version == NULL ) throw "-objc_abi_version missing version number"; - if ( strcmp(version, "2") == 0 ) - fObjCABIVersion2POverride = true; + if ( strcmp(version, "2") == 0 ) { + fObjCABIVersion1Override = false; + fObjCABIVersion2Override = true; + } + else if ( strcmp(version, "1") == 0 ) { + fObjCABIVersion1Override = true; + fObjCABIVersion2Override = false; + } else warning("ignoring unrecognized argument (%s) to -objc_abi_version", version); } + else if ( strcmp(arg, "-warn_weak_exports") == 0 ) { + fWarnWeakExports = true; + } + else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { + fObjcGcCompaction = true; + } + else if ( strcmp(arg, "-objc_gc") == 0 ) { + fObjCGc = true; + if ( fObjCGcOnly ) { + warning("-objc_gc overriding -objc_gc_only"); + fObjCGcOnly = false; + } + } + else if ( strcmp(arg, "-objc_gc_only") == 0 ) { + fObjCGcOnly = true; + if ( fObjCGc ) { + warning("-objc_gc_only overriding -objc_gc"); + fObjCGc = false; + } + } else if ( strcmp(arg, "-demangle") == 0 ) { - // <rdar://problem/8303976> add -demangle noop to ld64-97 + fDemangle = true; + } + else if ( strcmp(arg, "-version_load_command") == 0 ) { + fVersionLoadCommand = true; + } + else if ( strcmp(arg, "-no_version_load_command") == 0 ) { + fVersionLoadCommand = false; + } + else if ( strcmp(arg, "-function_starts") == 0 ) { + fFunctionStartsLoadCommand = true; + } + else if ( strcmp(arg, "-no_function_starts") == 0 ) { + fFunctionStartsLoadCommand = false; + } + else if ( strcmp(arg, "-object_path_lto") == 0 ) { + fTempLtoObjectPath = argv[++i]; + if ( fTempLtoObjectPath == NULL ) + throw "missing argument to -object_path_lto"; + } + else if ( strcmp(arg, "-no_objc_category_merging") == 0 ) { + fObjcCategoryMerging = false; + } + else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { + loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols); + } + else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { + loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols); + } + else if ( strcmp(arg, "-force_symbol_weak") == 0 ) { + const char* symbol = argv[++i]; + if ( symbol == NULL ) + throw "-force_symbol_weak missing <symbol>"; + fForceWeakSymbols.insert(symbol); + } + else if ( strcmp(arg, "-force_symbol_not_weak") == 0 ) { + const char* symbol = argv[++i]; + if ( symbol == NULL ) + throw "-force_symbol_not_weak missing <symbol>"; + fForceNotWeakSymbols.insert(symbol); + } + else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) { + if ( fExportMode == kExportSome ) + throw "can't use -exported_symbols_list and -reexported_symbols_list"; + loadExportFile(argv[++i], "-reexported_symbols_list", fReExportSymbols); + } + else if ( strcmp(arg, "-dyld_env") == 0 ) { + const char* envarg = argv[++i]; + if ( envarg == NULL ) + throw "-dyld_env missing ENV=VALUE"; + if ( strchr(envarg, '=') == NULL ) + throw "-dyld_env missing ENV=VALUE"; + fDyldEnvironExtras.push_back(envarg); } else { throwf("unknown option: %s", arg); @@ -2564,6 +2734,14 @@ void Options::buildSearchPaths(int argc, const char* argv[]) for(int i=0; i < argc; ++i) { if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) { const char* libSearchDir = &argv[i][2]; + // Allow either "-L{path}" or "-L {path}". + if (argv[i][2] == '\0') { + // -L {path}. Make sure there is an argument following this. + const char* path = argv[++i]; + if ( path == NULL ) + throw "-L missing argument"; + libSearchDir = path; + } if ( libSearchDir[0] == '\0' ) throw "-L must be immediately followed by a directory path (no space)"; struct stat statbuf; @@ -2574,11 +2752,19 @@ void Options::buildSearchPaths(int argc, const char* argv[]) warning("path '%s' following -L not a directory", libSearchDir); } else { - warning("directory '%s' following -L not found", libSearchDir); + warning("directory not found for option '-L%s'", libSearchDir); } } else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) { const char* frameworkSearchDir = &argv[i][2]; + // Allow either "-F{path}" or "-F {path}". + if (argv[i][2] == '\0') { + // -F {path}. Make sure there is an argument following this. + const char* path = argv[++i]; + if ( path == NULL ) + throw "-F missing argument"; + frameworkSearchDir = path; + } if ( frameworkSearchDir[0] == '\0' ) throw "-F must be immediately followed by a directory path (no space)"; struct stat statbuf; @@ -2589,7 +2775,7 @@ void Options::buildSearchPaths(int argc, const char* argv[]) warning("path '%s' following -F not a directory", frameworkSearchDir); } else { - warning("directory '%s' following -F not found", frameworkSearchDir); + warning("directory not found for option '-F%s'", frameworkSearchDir); } } else if ( strcmp(argv[i], "-Z") == 0 ) @@ -2600,9 +2786,9 @@ void Options::buildSearchPaths(int argc, const char* argv[]) fprintf(stderr, "%s", ldVersionString); // if only -v specified, exit cleanly if ( argc == 2 ) { -#if LTO_SUPPORT - printLTOVersion(*this); -#endif + const char* ltoVers = lto::version(); + if ( ltoVers != NULL ) + fprintf(stderr, "%s\n", ltoVers); exit(0); } } @@ -2613,9 +2799,11 @@ void Options::buildSearchPaths(int argc, const char* argv[]) fSDKPaths.push_back(path); } else if ( strcmp(argv[i], "-search_paths_first") == 0 ) { - // ??? Deprecate when we get -Bstatic/-Bdynamic. fLibrarySearchMode = kSearchDylibAndArchiveInEachDir; } + else if ( strcmp(argv[i], "-search_dylibs_first") == 0 ) { + fLibrarySearchMode = kSearchAllDirsForDylibsThenAllDirsForArchives; + } else if ( strcmp(argv[i], "-w") == 0 ) { sEmitWarnings = false; } @@ -2736,12 +2924,12 @@ void Options::parsePreCommandLineEnvironmentSettings() { if ((getenv("LD_TRACE_ARCHIVES") != NULL) || (getenv("RC_TRACE_ARCHIVES") != NULL)) - fReaderOptions.fTraceArchives = true; + fTraceArchives = true; if ((getenv("LD_TRACE_DYLIBS") != NULL) || (getenv("RC_TRACE_DYLIBS") != NULL)) { - fReaderOptions.fTraceDylibs = true; - fReaderOptions.fTraceIndirectDylibs = true; + fTraceDylibs = true; + fTraceIndirectDylibs = true; } if (getenv("RC_TRACE_DYLIB_SEARCHING") != NULL) { @@ -2751,8 +2939,8 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_PRINT_OPTIONS") != NULL) fPrintOptions = true; - if (fReaderOptions.fTraceDylibs || fReaderOptions.fTraceArchives) - fReaderOptions.fTraceOutputFile = getenv("LD_TRACE_FILE"); + if (fTraceDylibs || fTraceArchives) + fTraceOutputFile = getenv("LD_TRACE_FILE"); if (getenv("LD_PRINT_ORDER_FILE_STATISTICS") != NULL) fPrintOrderFileStatistics = true; @@ -2769,15 +2957,14 @@ void Options::parsePreCommandLineEnvironmentSettings() // for now disable compressed linkedit functionality if ( getenv("LD_NO_COMPACT_LINKEDIT") != NULL ) { fMakeCompressedDyldInfo = false; - fMakeClassicDyldInfo = true; - } - // temporary until projects adopt -no_pie - if ( getenv("LD_NO_PIE") != NULL ) { - warning("LD_NO_PIE being used to disble building a position independent executable"); - fDisablePositionIndependentExecutable = true; + fMakeCompressedDyldInfoForceOff = true; } sWarningsSideFilePath = getenv("LD_WARN_FILE"); + + const char* customDyldPath = getenv("LD_DYLD_PATH"); + if ( customDyldPath != NULL ) + fDyldInstallPath = customDyldPath; } @@ -2799,13 +2986,13 @@ void Options::parsePostCommandLineEnvironmentSettings() } // allow build system to force on dead-code-stripping - if ( fDeadStrip == kDeadStripOff ) { + if ( !fDeadStrip ) { if ( getenv("LD_DEAD_STRIP") != NULL ) { switch (fOutputKind) { case Options::kDynamicLibrary: case Options::kDynamicExecutable: case Options::kDynamicBundle: - fDeadStrip = kDeadStripOn; + fDeadStrip = true; break; case Options::kPreload: case Options::kObjectFile: @@ -2828,48 +3015,73 @@ void Options::reconfigureDefaults() // sync reader options switch ( fOutputKind ) { case Options::kObjectFile: - fReaderOptions.fForFinalLinkedImage = false; + fForFinalLinkedImage = false; break; case Options::kDyld: - fReaderOptions.fForDyld = true; - fReaderOptions.fForFinalLinkedImage = true; - fReaderOptions.fNoEHLabels = true; + fForDyld = true; + fForFinalLinkedImage = true; + fNoEHLabels = true; break; case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kKextBundle: - fReaderOptions.fForFinalLinkedImage = true; - fReaderOptions.fNoEHLabels = true; + fForFinalLinkedImage = true; + fNoEHLabels = true; break; case Options::kDynamicExecutable: case Options::kStaticExecutable: case Options::kPreload: - fReaderOptions.fLinkingMainExecutable = true; - fReaderOptions.fForFinalLinkedImage = true; - fReaderOptions.fNoEHLabels = true; + fLinkingMainExecutable = true; + fForFinalLinkedImage = true; + fNoEHLabels = true; break; } // set default min OS version - if ( (fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::kMinMacVersionUnset) - && (fReaderOptions.fIPhoneVersionMin == ObjectFile::ReaderOptions::kMinIPhoneVersionUnset) ) { + if ( (fMacVersionMin == ld::macVersionUnset) + && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) { // if neither -macosx_version_min nor -iphoneos_version_min used, try environment variables const char* macVers = getenv("MACOSX_DEPLOYMENT_TARGET"); const char* iPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + const char* iOSVers = getenv("IOS_DEPLOYMENT_TARGET"); if ( macVers != NULL ) setMacOSXVersionMin(macVers); else if ( iPhoneVers != NULL ) setIPhoneVersionMin(iPhoneVers); + else if ( iOSVers != NULL ) + setIPhoneVersionMin(iOSVers); else { // if still nothing, set default based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: case CPU_TYPE_POWERPC: - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_6; // FIX FIX, this really should be a check of the OS version the linker is running o + if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { + #ifdef DEFAULT_MACOSX_MIN_VERSION + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } break; case CPU_TYPE_ARM: - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) { + #if defined(DEFAULT_IPHONEOS_MIN_VERSION) + warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION); + setIPhoneVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); + #elif defined(DEFAULT_MACOSX_MIN_VERSION) + warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION); + setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); + #else + warning("-macosx_version_min not specificed, assuming 10.6"); + fMacVersionMin = ld::mac10_6; + #endif + } + break; + default: + // architecture will be infered ;ater by examining .o files break; } } @@ -2879,21 +3091,21 @@ void Options::reconfigureDefaults() // adjust min based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: - if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( (fMacVersionMin < ld::mac10_4) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) { //warning("-macosx_version_min should be 10.4 or later for i386"); - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; + fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_POWERPC64: - if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( fMacVersionMin < ld::mac10_4 ) { //warning("-macosx_version_min should be 10.4 or later for ppc64"); - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; + fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_X86_64: - if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_4 ) { + if ( fMacVersionMin < ld::mac10_4 ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); - fReaderOptions.fMacVersionMin = ObjectFile::ReaderOptions::k10_4; + fMacVersionMin = ld::mac10_4; } break; } @@ -2903,8 +3115,8 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_X86_64: // x86_64 uses new MH_KEXT_BUNDLE type - fMakeClassicDyldInfo = true; fMakeCompressedDyldInfo = false; + fMakeCompressedDyldInfoForceOff = true; fAllowTextRelocs = true; fUndefinedTreatment = kUndefinedDynamicLookup; break; @@ -2919,8 +3131,8 @@ void Options::reconfigureDefaults() // disable implicit dylibs when targeting 10.3 // <rdar://problem/5451987> add option to disable implicit load commands for indirectly used public dylibs - if ( !minOS(ObjectFile::ReaderOptions::k10_4, ObjectFile::ReaderOptions::k2_0) ) - fReaderOptions.fImplicitlyLinkPublicDylibs = false; + if ( !minOS(ld::mac10_4, ld::iPhone2_0) ) + fImplicitlyLinkPublicDylibs = false; // allow build system to force linker to ignore -prebind @@ -2979,6 +3191,39 @@ void Options::reconfigureDefaults() } } + // set too-large size + switch ( fArchitecture ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_I386: + fMaxAddress = 0xFFFFFFFF; + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + break; + case CPU_TYPE_ARM: + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + // user land code is limited to low 1GB + fMaxAddress = 0x2FFFFFFF; + break; + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + fMaxAddress = 0xFFFFFFFF; + break; + } + // range check -seg1addr for ARM + if ( fBaseAddress > fMaxAddress ) { + warning("ignoring -seg1addr 0x%08llX. Address out of range.", fBaseAddress); + fBaseAddress = 0; + } + break; + } + // <rdar://problem/6138961> -r implies no prebinding for all architectures if ( fOutputKind == Options::kObjectFile ) fPrebind = false; @@ -2988,12 +3233,12 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_POWERPC: case CPU_TYPE_I386: - if ( fReaderOptions.fMacVersionMin == ObjectFile::ReaderOptions::k10_4 ) { + if ( fMacVersionMin == ld::mac10_4 ) { // in 10.4 only split seg dylibs are prebound if ( (fOutputKind != Options::kDynamicLibrary) || ! fSplitSegs ) fPrebind = false; } - else if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) { + else if ( fMacVersionMin >= ld::mac10_5 ) { // in 10.5 nothing is prebound fPrebind = false; } @@ -3046,7 +3291,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { - if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k3_1) ) + if ( minOS(ld::mac10_5, ld::iPhone3_1) ) if ( !fPrebind ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) @@ -3058,7 +3303,7 @@ void Options::reconfigureDefaults() switch ( fArchitecture ) { case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table - if ( fReaderOptions.fMacVersionMin <= ObjectFile::ReaderOptions::k10_5 ) + if ( fMacVersionMin <= ld::mac10_5 ) fNeedsModuleTable = true; break; case CPU_TYPE_ARM: @@ -3070,7 +3315,7 @@ void Options::reconfigureDefaults() // <rdar://problem/5366363> -r -x implies -S if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) - fReaderOptions.fDebugInfoStripping = ObjectFile::ReaderOptions::kDebugInfoNone; + fDebugInfoStripping = Options::kDebugInfoNone; // choose how to process unwind info switch ( fArchitecture ) { @@ -3081,26 +3326,26 @@ void Options::reconfigureDefaults() case Options::kStaticExecutable: case Options::kPreload: case Options::kKextBundle: - fReaderOptions.fAddCompactUnwindEncoding = false; + fAddCompactUnwindEncoding = false; break; case Options::kDyld: case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kDynamicExecutable: - //if ( fReaderOptions.fAddCompactUnwindEncoding && (fReaderOptions.fVersionMin >= ObjectFile::ReaderOptions::k10_6) ) - // fReaderOptions.fRemoveDwarfUnwindIfCompactExists = true; + //if ( fAddCompactUnwindEncoding && (fVersionMin >= ld::mac10_6) ) + // fRemoveDwarfUnwindIfCompactExists = true; break; } break; case CPU_TYPE_POWERPC: case CPU_TYPE_POWERPC64: case CPU_TYPE_ARM: - fReaderOptions.fAddCompactUnwindEncoding = false; - fReaderOptions.fRemoveDwarfUnwindIfCompactExists = false; + fAddCompactUnwindEncoding = false; + fRemoveDwarfUnwindIfCompactExists = false; break; case 0: // if -arch is missing, assume we don't want compact unwind info - fReaderOptions.fAddCompactUnwindEncoding = false; + fAddCompactUnwindEncoding = false; break; } @@ -3113,7 +3358,7 @@ void Options::reconfigureDefaults() // don't move inits in dyld because dyld wants certain // entries point at stable locations at the start of __text if ( fOutputKind == Options::kDyld ) - fReaderOptions.fAutoOrderInitializers = false; + fAutoOrderInitializers = false; // disable __data ordering for some output kinds @@ -3131,6 +3376,24 @@ void Options::reconfigureDefaults() break; } + // only use compressed LINKEDIT for final linked images + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + case Options::kPreload: + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kDyld: + case Options::kKextBundle: + fMakeCompressedDyldInfoForceOff = true; + break; + } + if ( fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = false; + + // only use compressed LINKEDIT for: // x86_64 and i386 on Mac OS X 10.6 or later // arm on iPhoneOS 3.1 or later @@ -3138,15 +3401,11 @@ void Options::reconfigureDefaults() switch (fArchitecture) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: - if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_6 ) - fMakeClassicDyldInfo = false; - else if ( fReaderOptions.fMacVersionMin < ObjectFile::ReaderOptions::k10_5 ) + if ( fMacVersionMin < ld::mac10_6 ) fMakeCompressedDyldInfo = false; break; case CPU_TYPE_ARM: - if ( fReaderOptions.fIPhoneVersionMin >= ObjectFile::ReaderOptions::k3_1 ) - fMakeClassicDyldInfo = false; - else if ( fReaderOptions.fIPhoneVersionMin < ObjectFile::ReaderOptions::k3_1 ) + if ( !minOS(ld::mac10_6, ld::iPhone3_1) ) fMakeCompressedDyldInfo = false; break; case CPU_TYPE_POWERPC: @@ -3155,25 +3414,7 @@ void Options::reconfigureDefaults() fMakeCompressedDyldInfo = false; } } - - // only use compressed LINKEDIT for final linked images - if ( fMakeCompressedDyldInfo ) { - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - break; - case Options::kPreload: - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kDyld: - case Options::kKextBundle: - fMakeCompressedDyldInfo = false; - break; - } - } - fReaderOptions.fMakeCompressedDyldInfo = fMakeCompressedDyldInfo; // only ARM enforces that cpu-sub-types must match if ( fArchitecture != CPU_TYPE_ARM ) @@ -3181,7 +3422,11 @@ void Options::reconfigureDefaults() // only final linked images can not optimize zero fill sections if ( fOutputKind == Options::kObjectFile ) - fReaderOptions.fOptimizeZeroFill = true; + fOptimizeZeroFill = true; + + // all undefines in -r mode +// if ( fOutputKind == Options::kObjectFile ) +// fUndefinedTreatment = kUndefinedSuppress; // only dynamic final linked images should warn about use of commmons if ( fWarnCommons ) { @@ -3201,14 +3446,95 @@ void Options::reconfigureDefaults() } // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB - if ( minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) + if ( minOS(ld::mac10_5, ld::iPhone2_0) ) fUseSimplifiedDylibReExports = true; + + // Mac OS X 10.7 and iOS 4.2 support LC_LOAD_UPWARD_DYLIB + if ( minOS(ld::mac10_7, ld::iPhone4_2) && (fOutputKind == kDynamicLibrary) ) + fCanUseUpwardDylib = true; // x86_64 for MacOSX 10.7 defaults to PIE - if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) - && (fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_7) ) { + if ( (fArchitecture == CPU_TYPE_X86_64) && (fOutputKind == kDynamicExecutable) && (fMacVersionMin >= ld::mac10_7) ) { fPositionIndependentExecutable = true; } + + // armv7 for iOS4.3 defaults to PIE + if ( (fArchitecture == CPU_TYPE_ARM) + && (fSubArchitecture == CPU_SUBTYPE_ARM_V7) + && (fOutputKind == kDynamicExecutable) + && (fIPhoneVersionMin >= ld::iPhone4_3) ) { + fPositionIndependentExecutable = true; + } + + // -no_pie anywhere on command line disable PIE + if ( fDisablePositionIndependentExecutable ) + fPositionIndependentExecutable = false; + + // set fOutputSlidable + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + fOutputSlidable = false; + break; + case Options::kDynamicExecutable: + fOutputSlidable = fPositionIndependentExecutable; + break; + case Options::kPreload: + fOutputSlidable = fPIEOnCommandLine; + break; + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kKextBundle: + fOutputSlidable = true; + break; + } + + // let linker know if thread local variables are supported + if ( fMacVersionMin >= ld::mac10_7 ) { + fTLVSupport = true; + } + + // version load command is only in some kinds of output files + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kKextBundle: + fVersionLoadCommand = false; + fFunctionStartsLoadCommand = false; + break; + case Options::kDynamicExecutable: + case Options::kDyld: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + } + + // support re-export of individual symbols in MacOSX 10.7 and iOS 4.2 + if ( (fOutputKind == kDynamicLibrary) && minOS(ld::mac10_7, ld::iPhone4_2) ) + fCanReExportSymbols = true; + + // ObjC optimization is only in dynamic final linked images + switch ( fOutputKind ) { + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kKextBundle: + case Options::kDyld: + fObjcCategoryMerging = false; + break; + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + break; + } + + // i386 main executables linked on Mac OS X 10.7 default to NX heap + // regardless of target unless overriden with -allow_heap_execute anywhere + // on the command line + if ( (fArchitecture == CPU_TYPE_I386) && (fOutputKind == kDynamicExecutable) && !fDisableNonExecutableHeap) + fNonExecutableHeap = true; } void Options::checkIllegalOptionCombinations() @@ -3270,7 +3596,7 @@ void Options::checkIllegalOptionCombinations() // sync reader options if ( fNameSpace != kTwoLevelNameSpace ) - fReaderOptions.fFlatNamespace = true; + fFlatNamespace = true; // check -stack_addr if ( fStackAddr != 0 ) { @@ -3333,6 +3659,8 @@ void Options::checkIllegalOptionCombinations() case Options::kKextBundle: throw "-stack_size option can only be used when linking a main executable"; } + if ( fStackSize > fStackAddr ) + throwf("-stack_size (0x%08llX) must be smaller than -stack_addr (0x%08llX)", fStackSize, fStackAddr); } // check that -allow_stack_execute is only used with main executables @@ -3352,6 +3680,25 @@ void Options::checkIllegalOptionCombinations() } } + // check that -allow_heap_execute is only used with i386 main executables + if ( fDisableNonExecutableHeap ) { + if ( fArchitecture != CPU_TYPE_I386 ) + throw "-allow_heap_execute option can only be used when linking for i386"; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + // -allow_heap_execute only legal when building main executable + break; + case Options::kStaticExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + throw "-allow_heap_execute option can only be used when linking a main executable"; + } + } + // check -client_name is only used when making a bundle or main executable if ( fClientName != NULL ) { switch ( fOutputKind ) { @@ -3381,63 +3728,85 @@ void Options::checkIllegalOptionCombinations() throw "-dtrace can only be used when creating final linked images"; // check -d can only be used with -r - if ( fReaderOptions.fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) + if ( fMakeTentativeDefinitionsReal && (fOutputKind != Options::kObjectFile) ) throw "-d can only be used with -r"; // check that -root_safe is not used with -r - if ( fReaderOptions.fRootSafe && (fOutputKind == Options::kObjectFile) ) + if ( fRootSafe && (fOutputKind == Options::kObjectFile) ) throw "-root_safe cannot be used with -r"; // check that -setuid_safe is not used with -r - if ( fReaderOptions.fSetuidSafe && (fOutputKind == Options::kObjectFile) ) + if ( fSetuidSafe && (fOutputKind == Options::kObjectFile) ) throw "-setuid_safe cannot be used with -r"; + // rdar://problem/4718189 map ObjC class names to new runtime names + bool alterObjC1ClassNamesToObjC2 = false; + switch (fArchitecture) { + case CPU_TYPE_I386: + // i386 only uses new symbols when using objc2 ABI + if ( fObjCABIVersion2Override ) + alterObjC1ClassNamesToObjC2 = true; + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM: + alterObjC1ClassNamesToObjC2 = true; + break; + } + // make sure all required exported symbols exist std::vector<const char*> impliedExports; - for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); it++) { + for (NameSet::iterator it=fExportSymbols.regularBegin(); it != fExportSymbols.regularEnd(); ++it) { const char* name = *it; - // never export .eh symbols const int len = strlen(name); - if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) + if ( (strcmp(&name[len-3], ".eh") == 0) || (strncmp(name, ".objc_category_name_", 20) == 0) ) { + // never export .eh symbols warning("ignoring %s in export list", name); - else + } + else if ( (fArchitecture == CPU_TYPE_I386) && !fObjCABIVersion2Override && (strncmp(name, "_OBJC_CLASS_$", 13) == 0) ) { + warning("ignoring Objc2 Class symbol %s in i386 export list", name); + fRemovedExports.insert(name); + } + else if ( alterObjC1ClassNamesToObjC2 && (strncmp(name, ".objc_class_name_", 17) == 0) ) { + // linking ObjC2 ABI, but have ObjC1 ABI name in export list. Change it to intended name + fRemovedExports.insert(name); + char* temp; + asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]); + impliedExports.push_back(temp); + } + else { fInitialUndefines.push_back(name); - if ( strncmp(name, ".objc_class_name_", 17) == 0 ) { - // rdar://problem/4718189 map ObjC class names to new runtime names - switch (fArchitecture) { - case CPU_TYPE_I386: - // i386 only uses new symbols when using objc2 ABI - if ( !fObjCABIVersion2POverride ) - break; - // when using objc2 ABI to same as archs below - case CPU_TYPE_POWERPC64: - case CPU_TYPE_X86_64: - case CPU_TYPE_ARM: - char* temp; - asprintf(&temp, "_OBJC_CLASS_$_%s", &name[17]); - impliedExports.push_back(temp); - asprintf(&temp, "_OBJC_METACLASS_$_%s", &name[17]); - impliedExports.push_back(temp); - break; - } } } - for (std::vector<const char*>::iterator it=impliedExports.begin(); it != impliedExports.end(); it++) { + fExportSymbols.remove(fRemovedExports); + for (std::vector<const char*>::iterator it=impliedExports.begin(); it != impliedExports.end(); ++it) { const char* name = *it; fExportSymbols.insert(name); fInitialUndefines.push_back(name); } + // make sure all required re-exported symbols exist + for (NameSet::iterator it=fReExportSymbols.regularBegin(); it != fReExportSymbols.regularEnd(); ++it) { + fInitialUndefines.push_back(*it); + } + // make sure that -init symbol exist if ( fInitFunctionName != NULL ) fInitialUndefines.push_back(fInitFunctionName); + // make sure every alias base exists + for (std::vector<AliasPair>::iterator it=fAliases.begin(); it != fAliases.end(); ++it) { + fInitialUndefines.push_back(it->realName); + } + // check custom segments if ( fCustomSegmentAddresses.size() != 0 ) { // verify no segment is in zero page if ( fZeroPageSize != ULLONG_MAX ) { for (std::vector<SegmentStart>::iterator it = fCustomSegmentAddresses.begin(); it != fCustomSegmentAddresses.end(); ++it) { - if ( (it->address >= 0) && (it->address < fZeroPageSize) ) + if ( it->address < fZeroPageSize ) throwf("-segaddr %s 0x%llX conflicts with -pagezero_size", it->name, it->address); } } @@ -3464,7 +3833,7 @@ void Options::checkIllegalOptionCombinations() break; case CPU_TYPE_POWERPC64: // first 4GB for ppc64 on 10.5 - if ( fReaderOptions.fMacVersionMin >= ObjectFile::ReaderOptions::k10_5 ) + if ( fMacVersionMin >= ld::mac10_5 ) fZeroPageSize = 0x100000000ULL; else fZeroPageSize = 0x1000; // 10.4 dyld may not be able to handle >4GB zero page @@ -3495,13 +3864,21 @@ void Options::checkIllegalOptionCombinations() } } + // if main executable with custom base address, model zero page as custom segment + if ( (fOutputKind == Options::kDynamicExecutable) && (fBaseAddress != 0) && (fZeroPageSize != 0) ) { + SegmentStart seg; + seg.name = "__PAGEZERO"; + seg.address = 0;; + fCustomSegmentAddresses.push_back(seg); + } + // -dead_strip and -r are incompatible - if ( (fDeadStrip != kDeadStripOff) && (fOutputKind == Options::kObjectFile) ) + if ( fDeadStrip && (fOutputKind == Options::kObjectFile) ) throw "-r and -dead_strip cannot be used together"; // can't use -rpath unless targeting 10.5 or later if ( fRPaths.size() > 0 ) { - if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) + if ( !minOS(ld::mac10_5, ld::iPhone2_0) ) throw "-rpath can only be used when targeting Mac OS X 10.5 or later"; switch ( fOutputKind ) { case Options::kDynamicExecutable: @@ -3521,15 +3898,19 @@ void Options::checkIllegalOptionCombinations() if ( fPositionIndependentExecutable ) { switch ( fOutputKind ) { case Options::kDynamicExecutable: - // -no_pie anywhere on command line disable PIE - if ( fDisablePositionIndependentExecutable ) - fPositionIndependentExecutable = false; + if ( !minOS(ld::mac10_5, ld::iPhone4_2) ) { + if ( fIPhoneVersionMin == ld::iPhoneVersionUnset ) + throw "-pie can only be used when targeting Mac OS X 10.5 or later"; + else + throw "-pie can only be used when targeting iOS 4.2 or later"; + } break; case Options::kPreload: break; case Options::kDynamicLibrary: case Options::kDynamicBundle: warning("-pie being ignored. It is only used when linking a main executable"); + fPositionIndependentExecutable = false; break; case Options::kStaticExecutable: case Options::kObjectFile: @@ -3537,8 +3918,6 @@ void Options::checkIllegalOptionCombinations() case Options::kKextBundle: throw "-pie can only be used when linking a main executable"; } - if ( !minOS(ObjectFile::ReaderOptions::k10_5, ObjectFile::ReaderOptions::k2_0) ) - throw "-pie can only be used when targeting Mac OS X 10.5 or later"; } // check -read_only_relocs is not used with x86_64 @@ -3563,6 +3942,18 @@ void Options::checkIllegalOptionCombinations() warning("-force_cpusubtype_ALL will become unsupported for ARM architectures"); } } + + // -reexported_symbols_list can only be used with -dynamiclib + if ( !fReExportSymbols.empty() ) { + if ( fOutputKind != Options::kDynamicLibrary ) + throw "-reexported_symbols_list can only used used when created dynamic libraries"; + if ( !minOS(ld::mac10_7, ld::iPhone4_2) ) + throw "targeted OS version does not support -reexported_symbols_list"; + } + + // -dyld_env can only be used with main executables + if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) ) + throw "-dyld_env can only used used when created main executables"; } @@ -3578,9 +3969,20 @@ void Options::checkForClassic(int argc, const char* argv[]) bool newLinker = false; // build command line buffer in case ld crashes + const char* srcRoot = getenv("SRCROOT"); + if ( srcRoot != NULL ) { + strlcpy(crashreporterBuffer, "SRCROOT=", crashreporterBufferSize); + strlcat(crashreporterBuffer, srcRoot, crashreporterBufferSize); + strlcat(crashreporterBuffer, "\n", crashreporterBufferSize); + } +#ifdef LD_VERS + strlcat(crashreporterBuffer, LD_VERS, crashreporterBufferSize); + strlcat(crashreporterBuffer, "\n", crashreporterBufferSize); +#endif + strlcat(crashreporterBuffer, "ld ", crashreporterBufferSize); for(int i=1; i < argc; ++i) { - strlcat(crashreporterBuffer, argv[i], 1000); - strlcat(crashreporterBuffer, " ", 1000); + strlcat(crashreporterBuffer, argv[i], crashreporterBufferSize); + strlcat(crashreporterBuffer, " ", crashreporterBufferSize); } for(int i=0; i < argc; ++i) { @@ -3618,23 +4020,22 @@ void Options::checkForClassic(int argc, const char* argv[]) } } } - + // -dtrace only supported by new linker if( dtraceFound ) return; if( archFound ) { switch ( fArchitecture ) { - case CPU_TYPE_POWERPC: case CPU_TYPE_I386: - case CPU_TYPE_ARM: -// if ( staticFound && (rFound || !creatingMachKernel) ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_ARM: if ( (staticFound || kextFound) && !newLinker ) { // this environment variable will disable use of ld_classic for -static links if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) { // ld_classic does not support -iphoneos_version_min, so change for(int j=0; j < argc; ++j) { - if ( strcmp(argv[j], "-iphoneos_version_min") == 0) { + if ( (strcmp(argv[j], "-iphoneos_version_min") == 0) || (strcmp(argv[j], "-ios_version_min") == 0) ) { argv[j] = "-macosx_version_min"; if ( j < argc-1 ) argv[j+1] = "10.5"; @@ -3650,6 +4051,11 @@ void Options::checkForClassic(int argc, const char* argv[]) argv[j] = "-static"; } } + // ld classic does not understand -demangle + for(int j=0; j < argc; ++j) { + if ( strcmp(argv[j], "-demangle") == 0) + argv[j] = "-noprebind"; + } this->gotoClassicLinker(argc, argv); } } @@ -3667,6 +4073,15 @@ void Options::checkForClassic(int argc, const char* argv[]) void Options::gotoClassicLinker(int argc, const char* argv[]) { argv[0] = "ld_classic"; + // in -v mode, print command line passed to ld_classic + for(int i=0; i < argc; ++i) { + if ( strcmp(argv[i], "-v") == 0 ) { + for(int j=0; j < argc; ++j) + printf("%s ", argv[j]); + printf("\n"); + break; + } + } char rawPath[PATH_MAX]; char path[PATH_MAX]; uint32_t bufSize = PATH_MAX; diff --git a/src/ld/Options.h b/src/ld/Options.h index 4a06a5e..7ce5766 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -33,7 +33,7 @@ #include <ext/hash_set> #include <ext/hash_map> -#include "ObjectFile.h" +#include "ld.hpp" extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); @@ -41,12 +41,14 @@ extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2) class LibraryOptions { public: - LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), fLazyLoad(false), fForceLoad(false) {} + LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), + fLazyLoad(false), fUpward(false), fForceLoad(false) {} // for dynamic libraries bool fWeakImport; bool fReExport; bool fBundleLoader; bool fLazyLoad; + bool fUpward; // for static libraries bool fForceLoad; }; @@ -76,9 +78,9 @@ public: enum WeakReferenceMismatchTreatment { kWeakReferenceMismatchError, kWeakReferenceMismatchWeak, kWeakReferenceMismatchNonWeak }; enum CommonsMode { kCommonsIgnoreDylibs, kCommonsOverriddenByDylibs, kCommonsConflictsDylibsError }; - enum DeadStripMode { kDeadStripOff, kDeadStripOn, kDeadStripOnPlusUnusedInits }; enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; + enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; struct FileInfo { const char* path; @@ -93,6 +95,8 @@ public: const char* path; const uint8_t* data; uint64_t dataLen; + typedef ExtraSection* iterator; + typedef const ExtraSection* const_iterator; }; struct SectionAlignment { @@ -105,6 +109,7 @@ public: const char* symbolName; const char* objectFileName; }; + typedef const OrderedSymbol* OrderedSymbolsIterator; struct SegmentStart { const char* name; @@ -127,112 +132,164 @@ public: const char* useInstead; }; + struct AliasPair { + const char* realName; + const char* alias; + }; + + typedef const char* const* UndefinesIterator; - const ObjectFile::ReaderOptions& readerOptions(); - const char* getOutputFilePath(); - std::vector<FileInfo>& getInputFiles(); +// const ObjectFile::ReaderOptions& readerOptions(); + const char* outputFilePath() const { return fOutputFile; } + const std::vector<FileInfo>& getInputFiles() const { return fInputFiles; } - cpu_type_t architecture() { return fArchitecture; } - bool preferSubArchitecture() { return fHasPreferredSubType; } - cpu_subtype_t subArchitecture() { return fSubArchitecture; } - bool allowSubArchitectureMismatches() { return fAllowCpuSubtypeMismatches; } - OutputKind outputKind(); - bool prebind(); - bool bindAtLoad(); - bool fullyLoadArchives(); - NameSpace nameSpace(); - const char* installPath(); // only for kDynamicLibrary - uint32_t currentVersion(); // only for kDynamicLibrary - uint32_t compatibilityVersion(); // only for kDynamicLibrary - const char* entryName(); // only for kDynamicExecutable or kStaticExecutable + cpu_type_t architecture() const { return fArchitecture; } + bool preferSubArchitecture() const { return fHasPreferredSubType; } + cpu_subtype_t subArchitecture() const { return fSubArchitecture; } + bool allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; } + bool forceCpuSubtypeAll() const { return fForceSubtypeAll; } + const char* architectureName() const { return fArchitectureName; } + void setArchitecture(cpu_type_t, cpu_subtype_t subtype); + OutputKind outputKind() const { return fOutputKind; } + bool prebind() const { return fPrebind; } + bool bindAtLoad() const { return fBindAtLoad; } + NameSpace nameSpace() const { return fNameSpace; } + const char* installPath() const; // only for kDynamicLibrary + uint32_t currentVersion() const { return fDylibCurrentVersion; } // only for kDynamicLibrary + uint32_t compatibilityVersion() const { return fDylibCompatVersion; } // only for kDynamicLibrary + const char* entryName() const { return fEntryName; } // only for kDynamicExecutable or kStaticExecutable const char* executablePath(); - uint64_t baseAddress(); - bool keepPrivateExterns(); // only for kObjectFile - bool needsModuleTable(); // only for kDynamicLibrary - bool interposable(const char* name); - bool hasExportRestrictList(); // -exported_symbol or -unexported_symbol - bool hasExportMaskList(); // just -exported_symbol - bool hasWildCardExportRestrictList(); - bool allGlobalsAreDeadStripRoots(); - bool shouldExport(const char*); - bool ignoreOtherArchInputFiles(); - bool forceCpuSubtypeAll(); - bool traceDylibs(); - bool traceArchives(); - DeadStripMode deadStrip(); - UndefinedTreatment undefinedTreatment(); - ObjectFile::ReaderOptions::MacVersionMin macosxVersionMin() { return fReaderOptions.fMacVersionMin; } - ObjectFile::ReaderOptions::IPhoneVersionMin iphoneOSVersionMin() { return fReaderOptions.fIPhoneVersionMin; } - bool minOS(ObjectFile::ReaderOptions::MacVersionMin mac, ObjectFile::ReaderOptions::IPhoneVersionMin iPhoneOS); + uint64_t baseAddress() const { return fBaseAddress; } + uint64_t maxAddress() const { return fMaxAddress; } + bool keepPrivateExterns() const { return fKeepPrivateExterns; } // only for kObjectFile + bool needsModuleTable() const { return fNeedsModuleTable; } // only for kDynamicLibrary + bool interposable(const char* name) const; + bool hasExportRestrictList() const { return (fExportMode != kExportDefault); } // -exported_symbol or -unexported_symbol + bool hasExportMaskList() const { return (fExportMode == kExportSome); } // just -exported_symbol + bool hasWildCardExportRestrictList() const; + bool hasReExportList() const { return ! fReExportSymbols.empty(); } + bool wasRemovedExport(const char* sym) const { return ( fRemovedExports.find(sym) != fRemovedExports.end() ); } + bool allGlobalsAreDeadStripRoots() const; + bool shouldExport(const char*) const; + bool shouldReExport(const char*) const; + bool ignoreOtherArchInputFiles() const { return fIgnoreOtherArchFiles; } + bool traceDylibs() const { return fTraceDylibs; } + bool traceArchives() const { return fTraceArchives; } + bool deadCodeStrip() const { return fDeadStrip; } + UndefinedTreatment undefinedTreatment() const { return fUndefinedTreatment; } + ld::MacVersionMin macosxVersionMin() const { return fMacVersionMin; } + ld::IPhoneVersionMin iphoneOSVersionMin() const { return fIPhoneVersionMin; } + bool minOS(ld::MacVersionMin mac, ld::IPhoneVersionMin iPhoneOS); bool messagesPrefixedWithArchitecture(); Treatment picTreatment(); - WeakReferenceMismatchTreatment weakReferenceMismatchTreatment(); - const char* umbrellaName(); - std::vector<const char*>& allowableClients(); - const char* clientName(); - const char* initFunctionName(); // only for kDynamicLibrary + WeakReferenceMismatchTreatment weakReferenceMismatchTreatment() const { return fWeakReferenceMismatchTreatment; } + const char* umbrellaName() const { return fUmbrellaName; } + const std::vector<const char*>& allowableClients() const { return fAllowableClients; } + const char* clientName() const { return fClientName; } + const char* initFunctionName() const { return fInitFunctionName; } // only for kDynamicLibrary const char* dotOutputFile(); - uint64_t zeroPageSize(); - bool hasCustomStack(); - uint64_t customStackSize(); - uint64_t customStackAddr(); - bool hasExecutableStack(); - std::vector<const char*>& initialUndefines(); - bool printWhyLive(const char* name); - uint32_t minimumHeaderPad(); - uint64_t segmentAlignment() { return fSegmentAlignment; } - bool maxMminimumHeaderPad() { return fMaxMinimumHeaderPad; } - std::vector<ExtraSection>& extraSections(); - std::vector<SectionAlignment>& sectionAlignments(); - CommonsMode commonsMode(); - bool warnCommons(); + uint64_t pageZeroSize() const { return fZeroPageSize; } + bool hasCustomStack() const { return (fStackSize != 0); } + uint64_t customStackSize() const { return fStackSize; } + uint64_t customStackAddr() const { return fStackAddr; } + bool hasExecutableStack() const { return fExecutableStack; } + bool hasNonExecutableHeap() const { return fNonExecutableHeap; } + UndefinesIterator initialUndefinesBegin() const { return &fInitialUndefines[0]; } + UndefinesIterator initialUndefinesEnd() const { return &fInitialUndefines[fInitialUndefines.size()]; } + bool printWhyLive(const char* name) const; + uint32_t minimumHeaderPad() const { return fMinimumHeaderPad; } + bool maxMminimumHeaderPad() const { return fMaxMinimumHeaderPad; } + ExtraSection::const_iterator extraSectionsBegin() const { return &fExtraSections[0]; } + ExtraSection::const_iterator extraSectionsEnd() const { return &fExtraSections[fExtraSections.size()]; } + CommonsMode commonsMode() const { return fCommonsMode; } + bool warnCommons() const { return fWarnCommons; } bool keepRelocations(); - FileInfo findFile(const char* path); - UUIDMode getUUIDMode() { return fUUIDMode; } + FileInfo findFile(const char* path) const; + UUIDMode UUIDMode() const { return fUUIDMode; } bool warnStabs(); bool pauseAtEnd() { return fPause; } - bool printStatistics() { return fStatistics; } - bool printArchPrefix() { return fMessagesPrefixedWithArchitecture; } + bool printStatistics() const { return fStatistics; } + bool printArchPrefix() const { return fMessagesPrefixedWithArchitecture; } void gotoClassicLinker(int argc, const char* argv[]); - bool sharedRegionEligible() { return fSharedRegionEligible; } - bool printOrderFileStatistics() { return fPrintOrderFileStatistics; } + bool sharedRegionEligible() const { return fSharedRegionEligible; } + bool printOrderFileStatistics() const { return fPrintOrderFileStatistics; } const char* dTraceScriptName() { return fDtraceScriptName; } bool dTrace() { return (fDtraceScriptName != NULL); } - std::vector<OrderedSymbol>& orderedSymbols() { return fOrderedSymbols; } - bool splitSeg() { return fSplitSegs; } + unsigned long orderedSymbolsCount() const { return fOrderedSymbols.size(); } + OrderedSymbolsIterator orderedSymbolsBegin() const { return &fOrderedSymbols[0]; } + OrderedSymbolsIterator orderedSymbolsEnd() const { return &fOrderedSymbols[fOrderedSymbols.size()]; } + bool splitSeg() const { return fSplitSegs; } uint64_t baseWritableAddress() { return fBaseWritableAddress; } - std::vector<SegmentStart>& customSegmentAddresses() { return fCustomSegmentAddresses; } - std::vector<SegmentSize>& customSegmentSizes() { return fCustomSegmentSizes; } - std::vector<SegmentProtect>& customSegmentProtections() { return fCustomSegmentProtections; } - bool saveTempFiles() { return fSaveTempFiles; } - const std::vector<const char*>& rpaths() { return fRPaths; } + uint64_t segmentAlignment() const { return fSegmentAlignment; } + uint64_t segPageSize(const char* segName) const; + uint64_t customSegmentAddress(const char* segName) const; + bool hasCustomSegmentAddress(const char* segName) const; + bool hasCustomSectionAlignment(const char* segName, const char* sectName) const; + uint8_t customSectionAlignment(const char* segName, const char* sectName) const; + uint32_t initialSegProtection(const char*) const; + uint32_t maxSegProtection(const char*) const; + bool saveTempFiles() const { return fSaveTempFiles; } + const std::vector<const char*>& rpaths() const { return fRPaths; } bool readOnlyx86Stubs() { return fReadOnlyx86Stubs; } - std::vector<DylibOverride>& dylibOverrides() { return fDylibOverrides; } - const char* generatedMapPath() { return fMapPath; } - bool positionIndependentExecutable() { return fPositionIndependentExecutable; } - Options::FileInfo findFileUsingPaths(const char* path); - bool deadStripDylibs() { return fDeadStripDylibs; } - bool allowedUndefined(const char* name) { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } - bool someAllowedUndefines() { return (fAllowedUndefined.size() != 0); } + const std::vector<DylibOverride>& dylibOverrides() const { return fDylibOverrides; } + const char* generatedMapPath() const { return fMapPath; } + bool positionIndependentExecutable() const { return fPositionIndependentExecutable; } + Options::FileInfo findFileUsingPaths(const char* path) const; + bool deadStripDylibs() const { return fDeadStripDylibs; } + bool allowedUndefined(const char* name) const { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } + bool someAllowedUndefines() const { return (fAllowedUndefined.size() != 0); } LocalSymbolHandling localSymbolHandling() { return fLocalSymbolHandling; } - bool keepLocalSymbol(const char* symbolName); - bool allowTextRelocs() { return fAllowTextRelocs; } - bool warnAboutTextRelocs() { return fWarnTextRelocs; } - bool usingLazyDylibLinking() { return fUsingLazyDylibLinking; } - bool verbose() { return fVerbose; } - bool makeEncryptable() { return fEncryptable; } - bool needsUnwindInfoSection() { return fReaderOptions.fAddCompactUnwindEncoding; } - std::vector<const char*>& llvmOptions() { return fLLVMOptions; } - bool makeClassicDyldInfo() { return fMakeClassicDyldInfo; } - bool makeCompressedDyldInfo() { return fMakeCompressedDyldInfo; } + bool keepLocalSymbol(const char* symbolName) const; + bool allowTextRelocs() const { return fAllowTextRelocs; } + bool warnAboutTextRelocs() const { return fWarnTextRelocs; } + bool usingLazyDylibLinking() const { return fUsingLazyDylibLinking; } + bool verbose() const { return fVerbose; } + bool makeEncryptable() const { return fEncryptable; } + bool needsUnwindInfoSection() const { return fAddCompactUnwindEncoding; } + const std::vector<const char*>& llvmOptions() const{ return fLLVMOptions; } + const std::vector<const char*>& dyldEnvironExtras() const{ return fDyldEnvironExtras; } + bool makeCompressedDyldInfo() const { return fMakeCompressedDyldInfo; } bool hasExportedSymbolOrder(); - bool exportedSymbolOrder(const char* sym, unsigned int* order); + bool exportedSymbolOrder(const char* sym, unsigned int* order) const; bool orderData() { return fOrderData; } - bool errorOnOtherArchFiles() { return fErrorOnOtherArchFiles; } - bool markAutoDeadStripDylib() { return fMarkDeadStrippableDylib; } - bool removeEHLabels() { return fReaderOptions.fNoEHLabels; } - bool useSimplifiedDylibReExports() { return fUseSimplifiedDylibReExports; } - bool objCABIVersion2POverride() { return fObjCABIVersion2POverride; } + bool errorOnOtherArchFiles() const { return fErrorOnOtherArchFiles; } + bool markAutoDeadStripDylib() const { return fMarkDeadStrippableDylib; } + bool removeEHLabels() const { return fNoEHLabels; } + bool useSimplifiedDylibReExports() const { return fUseSimplifiedDylibReExports; } + bool objCABIVersion2POverride() const { return fObjCABIVersion2Override; } + bool useUpwardDylibs() const { return fCanUseUpwardDylib; } + bool fullyLoadArchives() const { return fFullyLoadArchives; } + bool loadAllObjcObjectsFromArchives() const { return fLoadAllObjcObjectsFromArchives; } + bool autoOrderInitializers() const { return fAutoOrderInitializers; } + bool optimizeZeroFill() const { return fOptimizeZeroFill; } + bool logAllFiles() const { return fLogAllFiles; } + DebugInfoStripping debugInfoStripping() const { return fDebugInfoStripping; } + bool flatNamespace() const { return fFlatNamespace; } + bool linkingMainExecutable() const { return fLinkingMainExecutable; } + bool implicitlyLinkIndirectPublicDylibs() const { return fImplicitlyLinkPublicDylibs; } + bool whyLoad() const { return fWhyLoad; } + const char* traceOutputFile() const { return fTraceOutputFile; } + bool outputSlidable() const { return fOutputSlidable; } + bool haveCmdLineAliases() const { return (fAliases.size() != 0); } + const std::vector<AliasPair>& cmdLineAliases() const { return fAliases; } + bool makeTentativeDefinitionsReal() const { return fMakeTentativeDefinitionsReal; } + const char* dyldInstallPath() const { return fDyldInstallPath; } + bool warnWeakExports() const { return fWarnWeakExports; } + bool objcGcCompaction() const { return fObjcGcCompaction; } + bool objcGc() const { return fObjCGc; } + bool objcGcOnly() const { return fObjCGcOnly; } + bool canUseThreadLocalVariables() const { return fTLVSupport; } + bool demangleSymbols() const { return fDemangle; } + bool addVersionLoadCommand() const { return fVersionLoadCommand; } + bool addFunctionStarts() const { return fFunctionStartsLoadCommand; } + bool canReExportSymbols() const { return fCanReExportSymbols; } + const char* tempLtoObjectPath() const { return fTempLtoObjectPath; } + bool objcCategoryMerging() const { return fObjcCategoryMerging; } + bool hasWeakBitTweaks() const; + bool forceWeak(const char* symbolName) const; + bool forceNotWeak(const char* symbolName) const; + bool forceWeakNonWildCard(const char* symbolName) const; + bool forceNotWeakNonWildcard(const char* symbolName) const; private: class CStringEquals @@ -249,14 +306,17 @@ private: class SetWithWildcards { public: void insert(const char*); - bool contains(const char*); - bool hasWildCards() { return !fWildCard.empty(); } - NameSet::iterator regularBegin() { return fRegular.begin(); } - NameSet::iterator regularEnd() { return fRegular.end(); } + bool contains(const char*) const; + bool containsNonWildcard(const char*) const; + bool empty() const { return fRegular.empty() && fWildCard.empty(); } + bool hasWildCards() const { return !fWildCard.empty(); } + NameSet::iterator regularBegin() const { return fRegular.begin(); } + NameSet::iterator regularEnd() const { return fRegular.end(); } + void remove(const NameSet&); private: static bool hasWildCards(const char*); - bool wildCardMatch(const char* pattern, const char* candidate); - bool inCharRange(const char*& range, unsigned char c); + bool wildCardMatch(const char* pattern, const char* candidate) const; + bool inCharRange(const char*& range, unsigned char c) const; NameSet fRegular; std::vector<const char*> fWildCard; @@ -271,7 +331,7 @@ private: FileInfo findFramework(const char* frameworkName); FileInfo findFramework(const char* rootName, const char* suffix); bool checkForFile(const char* format, const char* dir, const char* rootName, - FileInfo& result); + FileInfo& result) const; uint32_t parseVersionNumber(const char*); void parseSectionOrderFile(const char* segment, const char* section, const char* path); void parseOrderFile(const char* path, bool cstring); @@ -300,11 +360,13 @@ private: void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping); - ObjectFile::ReaderOptions fReaderOptions; + +// ObjectFile::ReaderOptions fReaderOptions; const char* fOutputFile; std::vector<Options::FileInfo> fInputFiles; cpu_type_t fArchitecture; cpu_subtype_t fSubArchitecture; + const char* fArchitectureName; OutputKind fOutputKind; bool fHasPreferredSubType; bool fPrebind; @@ -315,7 +377,7 @@ private: bool fErrorOnOtherArchFiles; bool fForceSubtypeAll; InterposeMode fInterposeMode; - DeadStripMode fDeadStrip; + bool fDeadStrip; NameSpace fNameSpace; uint32_t fDylibCompatVersion; uint32_t fDylibCurrentVersion; @@ -323,11 +385,16 @@ private: const char* fFinalName; const char* fEntryName; uint64_t fBaseAddress; + uint64_t fMaxAddress; uint64_t fBaseWritableAddress; bool fSplitSegs; SetWithWildcards fExportSymbols; SetWithWildcards fDontExportSymbols; SetWithWildcards fInterposeList; + SetWithWildcards fForceWeakSymbols; + SetWithWildcards fForceNotWeakSymbols; + SetWithWildcards fReExportSymbols; + NameSet fRemovedExports; NameToOrder fExportSymbolsOrder; ExportMode fExportMode; LibrarySearchMode fLibrarySearchMode; @@ -347,14 +414,18 @@ private: const char* fDtraceScriptName; const char* fSegAddrTablePath; const char* fMapPath; + const char* fDyldInstallPath; + const char* fTempLtoObjectPath; uint64_t fZeroPageSize; uint64_t fStackSize; uint64_t fStackAddr; bool fExecutableStack; + bool fNonExecutableHeap; + bool fDisableNonExecutableHeap; uint32_t fMinimumHeaderPad; uint64_t fSegmentAlignment; CommonsMode fCommonsMode; - UUIDMode fUUIDMode; + enum UUIDMode fUUIDMode; SetWithWildcards fLocalSymbolsIncluded; SetWithWildcards fLocalSymbolsExcluded; LocalSymbolHandling fLocalSymbolHandling; @@ -370,6 +441,7 @@ private: bool fPrintOrderFileStatistics; bool fReadOnlyx86Stubs; bool fPositionIndependentExecutable; + bool fPIEOnCommandLine; bool fDisablePositionIndependentExecutable; bool fMaxMinimumHeaderPad; bool fDeadStripDylibs; @@ -379,12 +451,52 @@ private: bool fEncryptable; bool fOrderData; bool fMarkDeadStrippableDylib; - bool fMakeClassicDyldInfo; bool fMakeCompressedDyldInfo; + bool fMakeCompressedDyldInfoForceOff; bool fNoEHLabels; bool fAllowCpuSubtypeMismatches; bool fUseSimplifiedDylibReExports; - bool fObjCABIVersion2POverride; + bool fObjCABIVersion2Override; + bool fObjCABIVersion1Override; + bool fCanUseUpwardDylib; + bool fFullyLoadArchives; + bool fLoadAllObjcObjectsFromArchives; + bool fFlatNamespace; + bool fLinkingMainExecutable; + bool fForFinalLinkedImage; + bool fForStatic; + bool fForDyld; + bool fMakeTentativeDefinitionsReal; + bool fWhyLoad; + bool fRootSafe; + bool fSetuidSafe; + bool fImplicitlyLinkPublicDylibs; + bool fAddCompactUnwindEncoding; + bool fWarnCompactUnwind; + bool fRemoveDwarfUnwindIfCompactExists; + bool fAutoOrderInitializers; + bool fOptimizeZeroFill; + bool fLogObjectFiles; + bool fLogAllFiles; + bool fTraceDylibs; + bool fTraceIndirectDylibs; + bool fTraceArchives; + bool fOutputSlidable; + bool fWarnWeakExports; + bool fObjcGcCompaction; + bool fObjCGc; + bool fObjCGcOnly; + bool fDemangle; + bool fTLVSupport; + bool fVersionLoadCommand; + bool fFunctionStartsLoadCommand; + bool fCanReExportSymbols; + bool fObjcCategoryMerging; + DebugInfoStripping fDebugInfoStripping; + const char* fTraceOutputFile; + ld::MacVersionMin fMacVersionMin; + ld::IPhoneVersionMin fIPhoneVersionMin; + std::vector<AliasPair> fAliases; std::vector<const char*> fInitialUndefines; NameSet fAllowedUndefined; NameSet fWhyLive; @@ -399,6 +511,7 @@ private: std::vector<const char*> fLibrarySearchPaths; std::vector<const char*> fFrameworkSearchPaths; std::vector<const char*> fSDKPaths; + std::vector<const char*> fDyldEnvironExtras; bool fSaveTempFiles; }; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp new file mode 100644 index 0000000..bb368c2 --- /dev/null +++ b/src/ld/OutputFile.cpp @@ -0,0 +1,3503 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <mach/mach_time.h> +#include <mach/vm_statistics.h> +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <uuid/uuid.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> +#include <mach-o/fat.h> + +#include <string> +#include <map> +#include <set> +#include <string> +#include <vector> +#include <list> +#include <algorithm> +#include <ext/hash_map> +#include <ext/hash_set> + +#include <CommonCrypto/CommonDigest.h> +#include <AvailabilityMacros.h> + +#include "MachOTrie.hpp" + +#include "Options.h" + +#include "OutputFile.h" +#include "Architectures.hpp" +#include "HeaderAndLoadCommands.hpp" +#include "LinkEdit.hpp" +#include "LinkEditClassic.hpp" + + +namespace ld { +namespace tool { + + +OutputFile::OutputFile(const Options& opts) + : + hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), + _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), + headerAndLoadCommandsSection(NULL), + rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), + lazyBindingSection(NULL), exportSection(NULL), + splitSegInfoSection(NULL), functionStartsSection(NULL), + symbolTableSection(NULL), stringPoolSection(NULL), + localRelocationsSection(NULL), externalRelocationsSection(NULL), + sectionRelocationsSection(NULL), + indirectSymbolTableSection(NULL), + _options(opts), + _hasDyldInfo(opts.makeCompressedDyldInfo()), + _hasSymbolTable(true), + _hasSectionRelocations(opts.outputKind() == Options::kObjectFile), + _hasSplitSegInfo(opts.sharedRegionEligible()), + _hasFunctionStartsInfo(opts.addFunctionStarts()), + _hasDynamicSymbolTable(true), + _hasLocalRelocations(!opts.makeCompressedDyldInfo()), + _hasExternalRelocations(!opts.makeCompressedDyldInfo()), + _encryptedTEXTstartOffset(0), + _encryptedTEXTendOffset(0), + _localSymbolsStartIndex(0), + _localSymbolsCount(0), + _globalSymbolsStartIndex(0), + _globalSymbolsCount(0), + _importSymbolsStartIndex(0), + _importSymbolsCount(0), + _sectionsRelocationsAtom(NULL), + _localRelocsAtom(NULL), + _externalRelocsAtom(NULL), + _symbolTableAtom(NULL), + _indirectSymbolTableAtom(NULL), + _rebasingInfoAtom(NULL), + _bindingInfoAtom(NULL), + _lazyBindingInfoAtom(NULL), + _weakBindingInfoAtom(NULL), + _exportInfoAtom(NULL), + _splitSegInfoAtom(NULL), + _functionStartsAtom(NULL) +{ +} + +void OutputFile::dumpAtomsBySection(ld::Internal& state, bool printAtoms) +{ + fprintf(stderr, "SORTED:\n"); + for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + fprintf(stderr, "final section %p %s/%s %s start addr=0x%08llX, size=0x%08llX, alignment=%02d, fileOffset=0x%08llX\n", + (*it), (*it)->segmentName(), (*it)->sectionName(), (*it)->isSectionHidden() ? "(hidden)" : "", + (*it)->address, (*it)->size, (*it)->alignment, (*it)->fileOffset); + if ( printAtoms ) { + std::vector<const ld::Atom*>& atoms = (*it)->atoms; + for (std::vector<const ld::Atom*>::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { + fprintf(stderr, " %p (0x%04llX) %s\n", *ait, (*ait)->size(), (*ait)->name()); + } + } + } + fprintf(stderr, "DYLIBS:\n"); + for (std::vector<ld::dylib::File*>::iterator it=state.dylibs.begin(); it != state.dylibs.end(); ++it ) + fprintf(stderr, " %s\n", (*it)->installPath()); +} + +void OutputFile::write(ld::Internal& state) +{ + this->buildDylibOrdinalMapping(state); + this->addLoadCommands(state); + this->addLinkEdit(state); + this->setSectionSizesAndAlignments(state); + this->setLoadCommandsPadding(state); + this->assignFileOffsets(state); + this->assignAtomAddresses(state); + this->synthesizeDebugNotes(state); + this->buildSymbolTable(state); + this->generateLinkEditInfo(state); + this->makeSplitSegInfo(state); + this->updateLINKEDITAddresses(state); + //this->dumpAtomsBySection(state, false); + this->writeOutputFile(state); + this->writeMapFile(state); +} + +bool OutputFile::findSegment(ld::Internal& state, uint64_t addr, uint64_t* start, uint64_t* end, uint32_t* index) +{ + uint32_t segIndex = 0; + ld::Internal::FinalSection* segFirstSection = NULL; + ld::Internal::FinalSection* lastSection = NULL; + for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( (segFirstSection == NULL ) || strcmp(segFirstSection->segmentName(), sect->segmentName()) != 0 ) { + if ( segFirstSection != NULL ) { + //fprintf(stderr, "findSegment(0x%llX) seg changed to %s\n", addr, sect->segmentName()); + if ( (addr >= segFirstSection->address) && (addr < lastSection->address+lastSection->size) ) { + *start = segFirstSection->address; + *end = lastSection->address+lastSection->size; + *index = segIndex; + return true; + } + ++segIndex; + } + segFirstSection = sect; + } + lastSection = sect; + } + return false; +} + + +void OutputFile::assignAtomAddresses(ld::Internal& state) +{ + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + switch ( sect-> type() ) { + case ld::Section::typeImportProxies: + // want finalAddress() of all proxy atoms to be zero + (const_cast<ld::Atom*>(atom))->setSectionStartAddress(0); + break; + case ld::Section::typeAbsoluteSymbols: + // want finalAddress() of all absolute atoms to be value of abs symbol + (const_cast<ld::Atom*>(atom))->setSectionStartAddress(0); + break; + case ld::Section::typeLinkEdit: + // linkedit layout is assigned later + break; + default: + (const_cast<ld::Atom*>(atom))->setSectionStartAddress(sect->address); + break; + } + } + } +} + +void OutputFile::updateLINKEDITAddresses(ld::Internal& state) +{ + if ( _options.makeCompressedDyldInfo() ) { + // build dylb rebasing info + assert(_rebasingInfoAtom != NULL); + _rebasingInfoAtom->encode(); + + // build dyld binding info + assert(_bindingInfoAtom != NULL); + _bindingInfoAtom->encode(); + + // build dyld lazy binding info + assert(_lazyBindingInfoAtom != NULL); + _lazyBindingInfoAtom->encode(); + + // build dyld weak binding info + assert(_weakBindingInfoAtom != NULL); + _weakBindingInfoAtom->encode(); + + // build dyld export info + assert(_exportInfoAtom != NULL); + _exportInfoAtom->encode(); + } + + if ( _options.sharedRegionEligible() ) { + // build split seg info + assert(_splitSegInfoAtom != NULL); + _splitSegInfoAtom->encode(); + } + + if ( _options.addFunctionStarts() ) { + // build function starts info + assert(_functionStartsAtom != NULL); + _functionStartsAtom->encode(); + } + + // build classic symbol table + assert(_symbolTableAtom != NULL); + _symbolTableAtom->encode(); + assert(_indirectSymbolTableAtom != NULL); + _indirectSymbolTableAtom->encode(); + + // add relocations to .o files + if ( _options.outputKind() == Options::kObjectFile ) { + assert(_sectionsRelocationsAtom != NULL); + _sectionsRelocationsAtom->encode(); + } + + if ( ! _options.makeCompressedDyldInfo() ) { + // build external relocations + assert(_externalRelocsAtom != NULL); + _externalRelocsAtom->encode(); + // build local relocations + assert(_localRelocsAtom != NULL); + _localRelocsAtom->encode(); + } + + // update address and file offsets now that linkedit content has been generated + uint64_t curLinkEditAddress = 0; + uint64_t curLinkEditfileOffset = 0; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeLinkEdit ) + continue; + if ( curLinkEditAddress == 0 ) { + curLinkEditAddress = sect->address; + curLinkEditfileOffset = sect->fileOffset; + } + uint16_t maxAlignment = 0; + uint64_t offset = 0; + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + //fprintf(stderr, "setting linkedit atom offset for %s\n", atom->name()); + if ( atom->alignment().powerOf2 > maxAlignment ) + maxAlignment = atom->alignment().powerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atom->alignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + (const_cast<ld::Atom*>(atom))->setSectionOffset(offset); + (const_cast<ld::Atom*>(atom))->setSectionStartAddress(curLinkEditAddress); + offset += atom->size(); + } + sect->size = offset; + // section alignment is that of a contained atom with the greatest alignment + sect->alignment = maxAlignment; + sect->address = curLinkEditAddress; + sect->fileOffset = curLinkEditfileOffset; + curLinkEditAddress += sect->size; + curLinkEditfileOffset += sect->size; + } + + _fileSize = state.sections.back()->fileOffset + state.sections.back()->size; +} + +void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) +{ + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { + // absolute symbols need their finalAddress() to their value + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + (const_cast<ld::Atom*>(atom))->setSectionOffset(atom->objectAddress()); + } + } + else { + uint16_t maxAlignment = 0; + uint64_t offset = 0; + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->alignment().powerOf2 > maxAlignment ) + maxAlignment = atom->alignment().powerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atom->alignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + // LINKEDIT atoms are laid out later + if ( sect->type() != ld::Section::typeLinkEdit ) { + (const_cast<ld::Atom*>(atom))->setSectionOffset(offset); + offset += atom->size(); + } + if ( (atom->scope() == ld::Atom::scopeGlobal) + && (atom->definition() == ld::Atom::definitionRegular) + && (atom->combine() == ld::Atom::combineByName) + && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) + || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { + this->hasWeakExternalSymbols = true; + if ( _options.warnWeakExports() ) + warning("weak external symbol: %s", atom->name()); + } + } + sect->size = offset; + // section alignment is that of a contained atom with the greatest alignment + sect->alignment = maxAlignment; + // unless -sectalign command line option overrides + if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) + sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); + // each atom in __eh_frame has zero alignment to assure they pack together, + // but compilers usually make the CFIs pointer sized, so we want whole section + // to start on pointer sized boundary. + if ( sect->type() == ld::Section::typeCFI ) + sect->alignment = 3; + if ( sect->type() == ld::Section::typeTLVDefs ) + this->hasThreadLocalVariableDefinitions = true; + } + } +} + +void OutputFile::setLoadCommandsPadding(ld::Internal& state) +{ + // In other sections, any extra space is put and end of segment. + // In __TEXT segment, any extra space is put after load commands to allow post-processing of load commands + // Do a reverse layout of __TEXT segment to determine padding size and adjust section size + uint64_t paddingSize = 0; + switch ( _options.outputKind() ) { + case Options::kDyld: + // dyld itself has special padding requirements. We want the beginning __text section to start at a stable address + assert(strcmp(state.sections[1]->sectionName(),"__text") == 0); + state.sections[1]->alignment = 12; // page align __text + break; + case Options::kObjectFile: + // mach-o .o files need no padding between load commands and first section + // but leave enough room that the object file could be signed + paddingSize = 32; + break; + case Options::kPreload: + // mach-o MH_PRELOAD files need no padding between load commands and first section + paddingSize = 0; + default: + // work backwards from end of segment and lay out sections so that extra room goes to padding atom + uint64_t addr = 0; + for (std::vector<ld::Internal::FinalSection*>::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( strcmp(sect->segmentName(), "__TEXT") != 0 ) + continue; + if ( sect == headerAndLoadCommandsSection ) { + addr -= headerAndLoadCommandsSection->size; + paddingSize = addr % _options.segmentAlignment(); + break; + } + addr -= sect->size; + addr = addr & (0 - (1 << sect->alignment)); + } + + // if command line requires more padding than this + uint32_t minPad = _options.minimumHeaderPad(); + if ( _options.maxMminimumHeaderPad() ) { + // -headerpad_max_install_names means there should be room for every path load command to grow to 1204 bytes + uint32_t altMin = _dylibsToLoad.size() * MAXPATHLEN; + if ( _options.outputKind() == Options::kDynamicLibrary ) + altMin += MAXPATHLEN; + if ( altMin > minPad ) + minPad = altMin; + } + if ( paddingSize < minPad ) { + int extraPages = (minPad - paddingSize + _options.segmentAlignment() - 1)/_options.segmentAlignment(); + paddingSize += extraPages * _options.segmentAlignment(); + } + + if ( _options.makeEncryptable() ) { + // load commands must be on a separate non-encrypted page + int loadCommandsPage = (headerAndLoadCommandsSection->size + minPad)/_options.segmentAlignment(); + int textPage = (headerAndLoadCommandsSection->size + paddingSize)/_options.segmentAlignment(); + if ( loadCommandsPage == textPage ) { + paddingSize += _options.segmentAlignment(); + textPage += 1; + } + // remember start for later use by load command + _encryptedTEXTstartOffset = textPage*_options.segmentAlignment(); + } + break; + } + // add padding to size of section + headerAndLoadCommandsSection->size += paddingSize; +} + + +uint64_t OutputFile::pageAlign(uint64_t addr) +{ + const uint64_t alignment = _options.segmentAlignment(); + return ((addr+alignment-1) & (-alignment)); +} + +uint64_t OutputFile::pageAlign(uint64_t addr, uint64_t pageSize) +{ + return ((addr+pageSize-1) & (-pageSize)); +} + + +void OutputFile::assignFileOffsets(ld::Internal& state) +{ + const bool log = false; + const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kPreload)); + const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); + + uint64_t address = 0; + const char* lastSegName = ""; + uint64_t floatingAddressStart = _options.baseAddress(); + + // first pass, assign addresses to sections in segments with fixed start addresses + if ( log ) fprintf(stderr, "Fixed address segments:\n"); + for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + address = _options.customSegmentAddress(sect->segmentName()); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + + // if TEXT segment address is fixed, then flow other segments after it + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + floatingAddressStart = address; + } + } + + // second pass, assign section address to sections in segments that are contiguous with previous segment + address = floatingAddressStart; + lastSegName = ""; + if ( log ) fprintf(stderr, "Regular layout segments:\n"); + for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { + sect->alignmentPaddingBytes = 0; + continue; + } + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + // round up size of last segment if needed + if ( *lastSegName != '\0' ) { + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // set segment address based on end of last segment + address = pageAlign(address); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + } + + + // third pass, assign section file offsets + uint64_t fileOffset = 0; + lastSegName = ""; + if ( log ) fprintf(stderr, "All segments with file offsets:\n"); + for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( hasZeroForFileOffset(sect) ) { + // fileoff of zerofill sections is moot, but historically it is set to zero + sect->fileOffset = 0; + } + else { + // page align file offset at start of each segment + if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { + fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); + } + lastSegName = sect->segmentName(); + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + + // update section info + sect->fileOffset = fileOffset; + + // update running total + fileOffset += sect->size; + } + + if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", + sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, + sect->segmentName(), sect->sectionName()); + } + + + // for encrypted iPhoneOS apps + if ( _options.makeEncryptable() ) { + // remember end of __TEXT for later use by load command + for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + _encryptedTEXTendOffset = pageAlign(sect->fileOffset); + } + } + } + + // remember total file size + _fileSize = fileOffset; +} + + +static const char* makeName(const ld::Atom& atom) +{ + static char buffer[4096]; + switch ( atom.symbolTableInclusion() ) { + case ld::Atom::symbolTableNotIn: + case ld::Atom::symbolTableNotInFinalLinkedImages: + sprintf(buffer, "%s@0x%08llX", atom.name(), atom.objectAddress()); + break; + case ld::Atom::symbolTableIn: + case ld::Atom::symbolTableInAndNeverStrip: + case ld::Atom::symbolTableInAsAbsolute: + case ld::Atom::symbolTableInWithRandomAutoStripLabel: + strlcpy(buffer, atom.name(), 4096); + break; + } + return buffer; +} + +static const char* referenceTargetAtomName(ld::Internal& state, const ld::Fixup* ref) +{ + switch ( ref->binding ) { + case ld::Fixup::bindingNone: + return "NO BINDING"; + case ld::Fixup::bindingByNameUnbound: + return (char*)(ref->u.target); + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + return makeName(*((ld::Atom*)(ref->u.target))); + case ld::Fixup::bindingsIndirectlyBound: + return makeName(*state.indirectBindingTable[ref->u.bindingIndex]); + } + return "BAD BINDING"; +} + +bool OutputFile::targetIsThumb(ld::Internal& state, const ld::Fixup* fixup) +{ + switch ( fixup->binding ) { + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + return fixup->u.target->isThumb(); + case ld::Fixup::bindingsIndirectlyBound: + return state.indirectBindingTable[fixup->u.bindingIndex]->isThumb(); + default: + break; + } + throw "unexpected binding"; +} + +uint64_t OutputFile::addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target) +{ + if ( !_options.makeCompressedDyldInfo() ) { + // For external relocations the classic mach-o format + // has addend only stored in the content. That means + // that the address of the target is not used. + if ( fixup->contentAddendOnly ) + return 0; + } + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + *target = fixup->u.target; + return (*target)->finalAddress(); + case ld::Fixup::bindingsIndirectlyBound: + *target = state.indirectBindingTable[fixup->u.bindingIndex]; + return (*target)->finalAddress(); + } + throw "unexpected binding"; +} + +uint64_t OutputFile::sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup) +{ + const ld::Atom* target = NULL; + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fixup->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fixup->u.bindingIndex]; + break; + } + assert(target != NULL); + + uint64_t targetAddress = target->finalAddress(); + for (std::vector<ld::Internal::FinalSection*>::const_iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + const ld::Internal::FinalSection* sect = *it; + if ( (sect->address <= targetAddress) && (targetAddress < (sect->address+sect->size)) ) + return targetAddress - sect->address; + } + throw "section not found for section offset"; +} + + + +uint64_t OutputFile::tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup) +{ + const ld::Atom* target = NULL; + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fixup->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fixup->u.bindingIndex]; + break; + } + assert(target != NULL); + + for (std::vector<ld::Internal::FinalSection*>::const_iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + const ld::Internal::FinalSection* sect = *it; + switch ( sect->type() ) { + case ld::Section::typeTLVInitialValues: + case ld::Section::typeTLVZeroFill: + return target->finalAddress() - sect->address; + default: + break; + } + } + throw "section not found for tlvTemplateOffsetOf"; +} + +void OutputFile::printSectionLayout(ld::Internal& state) +{ + // show layout of final image + fprintf(stderr, "final section layout:\n"); + for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + if ( (*it)->isSectionHidden() ) + continue; + fprintf(stderr, " %s/%s addr=0x%08llX, size=0x%08llX, fileOffset=0x%08llX, type=%d\n", + (*it)->segmentName(), (*it)->sectionName(), + (*it)->address, (*it)->size, (*it)->fileOffset, (*it)->type()); + } +} + + +void OutputFile::rangeCheck8(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( (displacement > 127) || (displacement < -128) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("8-bit reference out of range (%lld max is +/-127B): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheck16(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t thirtyTwoKLimit = 0x00007FFF; + if ( (displacement > thirtyTwoKLimit) || (displacement < (-thirtyTwoKLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("16-bit reference out of range (%lld max is +/-32KB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckBranch32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("32-bit branch out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckRIP32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t twoGigLimit = 0x7FFFFFFF; + if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("32-bit RIP relative reference out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckARM12(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( (displacement > 4092LL) || (displacement < (-4092LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("ARM ldr 12-bit displacement out of range (%lld max is +/-4096B): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + + +void OutputFile::rangeCheckARMBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + // armv7 supports a larger displacement + if ( _options.preferSubArchitecture() && (_options.subArchitecture() == CPU_SUBTYPE_ARM_V7) ) { + if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } + } + else { + if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } + } +} + +void OutputFile::rangeCheckPPCBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t bl_eightMegLimit = 0x00FFFFFF; + if ( (displacement > bl_eightMegLimit) || (displacement < (-bl_eightMegLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("bl PPC branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + +void OutputFile::rangeCheckPPCBranch14(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t b_sixtyFourKiloLimit = 0x0000FFFF; + if ( (displacement > b_sixtyFourKiloLimit) || (displacement < (-b_sixtyFourKiloLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("bcc PPC branch out of range (%lld max is +/-64KB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} + + + + +uint16_t OutputFile::get16LE(uint8_t* loc) { return LittleEndian::get16(*(uint16_t*)loc); } +void OutputFile::set16LE(uint8_t* loc, uint16_t value) { LittleEndian::set16(*(uint16_t*)loc, value); } + +uint32_t OutputFile::get32LE(uint8_t* loc) { return LittleEndian::get32(*(uint32_t*)loc); } +void OutputFile::set32LE(uint8_t* loc, uint32_t value) { LittleEndian::set32(*(uint32_t*)loc, value); } + +uint64_t OutputFile::get64LE(uint8_t* loc) { return LittleEndian::get64(*(uint64_t*)loc); } +void OutputFile::set64LE(uint8_t* loc, uint64_t value) { LittleEndian::set64(*(uint64_t*)loc, value); } + +uint16_t OutputFile::get16BE(uint8_t* loc) { return BigEndian::get16(*(uint16_t*)loc); } +void OutputFile::set16BE(uint8_t* loc, uint16_t value) { BigEndian::set16(*(uint16_t*)loc, value); } + +uint32_t OutputFile::get32BE(uint8_t* loc) { return BigEndian::get32(*(uint32_t*)loc); } +void OutputFile::set32BE(uint8_t* loc, uint32_t value) { BigEndian::set32(*(uint32_t*)loc, value); } + +uint64_t OutputFile::get64BE(uint8_t* loc) { return BigEndian::get64(*(uint64_t*)loc); } +void OutputFile::set64BE(uint8_t* loc, uint64_t value) { BigEndian::set64(*(uint64_t*)loc, value); } + +void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer) +{ + //fprintf(stderr, "applyFixUps() on %s\n", atom->name()); + int64_t accumulator = 0; + const ld::Atom* toTarget = NULL; + const ld::Atom* fromTarget; + int64_t delta; + uint32_t instruction; + uint32_t newInstruction; + uint16_t instructionLowHalf; + bool is_bl; + bool is_blx; + bool is_b; + bool thumbTarget = false; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + uint8_t* fixUpLocation = &buffer[fit->offsetInAtom]; + switch ( (ld::Fixup::Kind)(fit->kind) ) { + case ld::Fixup::kindNone: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: + break; + case ld::Fixup::kindSetTargetAddress: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentAddendOnly || fit->contentDetlaToAddendOnly ) + accumulator = 0; + break; + case ld::Fixup::kindSubtractTargetAddress: + delta = addressOf(state, fit, &fromTarget); + if ( ! fit->contentAddendOnly ) + accumulator -= delta; + break; + case ld::Fixup::kindAddAddend: + // <rdar://problem/8342028> ARM main executables main contain .long constants pointing + // into themselves such as jump tables. These .long should not have thumb bit set + // even though the target is a thumb instruction. We can tell it is an interior pointer + // because we are processing an addend. + if ( thumbTarget && (toTarget == atom) && ((int32_t)fit->u.addend > 0) ) { + accumulator &= (-2); + //warning("removing thumb bit from intra-atom pointer in %s %s+0x%0X", + // atom->section().sectionName(), atom->name(), fit->offsetInAtom); + } + accumulator += fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + accumulator -= fit->u.addend; + break; + case ld::Fixup::kindSetTargetImageOffset: + accumulator = addressOf(state, fit, &toTarget) - mhAddress; + break; + case ld::Fixup::kindSetTargetSectionOffset: + accumulator = sectionOffsetOf(state, fit); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffset: + accumulator = tlvTemplateOffsetOf(state, fit); + break; + case ld::Fixup::kindStore8: + *fixUpLocation += accumulator; + break; + case ld::Fixup::kindStoreLittleEndian16: + set16LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreLittleEndianLow24of32: + set32LE(fixUpLocation, (get32LE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); + break; + case ld::Fixup::kindStoreLittleEndian32: + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreLittleEndian64: + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreBigEndian16: + set16BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreBigEndianLow24of32: + set32BE(fixUpLocation, (get32BE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) ); + break; + case ld::Fixup::kindStoreBigEndian32: + set32BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreBigEndian64: + set64BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreX86PCRel8: + case ld::Fixup::kindStoreX86BranchPCRel8: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 1); + rangeCheck8(delta, state, atom, fit); + *fixUpLocation = delta; + break; + case ld::Fixup::kindStoreX86PCRel16: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 2); + rangeCheck16(delta, state, atom, fit); + set16LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86BranchPCRel32: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckBranch32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32_1: + if ( fit->contentAddendOnly ) + delta = accumulator - 1; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 5); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32_2: + if ( fit->contentAddendOnly ) + delta = accumulator - 2; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 6); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32_4: + if ( fit->contentAddendOnly ) + delta = accumulator - 4; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA: + assert(_options.outputKind() != Options::kObjectFile); + // TLV entry was optimized away, change movl instruction to a leal + if ( fixUpLocation[-1] != 0xA1 ) + throw "TLV load reloc does not point to a movl instruction"; + fixUpLocation[-1] = 0xB8; + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + assert(_options.outputKind() != Options::kObjectFile); + // GOT entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "GOT load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + assert(_options.outputKind() != Options::kObjectFile); + // TLV entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "TLV load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressARMLoad12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARMLoad12 case + case ld::Fixup::kindStoreARMLoad12: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); + rangeCheckARM12(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + if ( delta >= 0 ) { + newInstruction = instruction & 0xFFFFF000; + newInstruction |= ((uint32_t)delta & 0xFFF); + } + else { + newInstruction = instruction & 0xFF7FF000; + newInstruction |= ((uint32_t)(-delta) & 0xFFF); + } + set32LE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCBranch14: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckPPCBranch14(delta, state, atom, fit); + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0003) | ((uint32_t)delta & 0x0000FFFC); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCAbsLow14: + instruction = get32BE(fixUpLocation); + if ( (accumulator & 0x3) != 0 ) + throwf("bad offset (0x%08X) for lo14 instruction pic-base fix-up", (uint32_t)accumulator); + newInstruction = (instruction & 0xFFFF0003) | (accumulator & 0xFFFC); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsLow16: + case ld::Fixup::kindStorePPCPicLow16: + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | (accumulator & 0xFFFF); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCPicHigh16AddLow: + instructionLowHalf = (accumulator >> 16) & 0xFFFF; + if ( accumulator & 0x00008000 ) + ++instructionLowHalf; + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | instructionLowHalf; + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStorePPCAbsHigh16: + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFFFF0000) | ((accumulator >> 16) & 0xFFFF); + set32BE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindDtraceExtra: + break; + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + fixUpLocation[-1] = 0x90; // 1-byte nop + fixUpLocation[0] = 0x0F; // 4-byte nop + fixUpLocation[1] = 0x1F; + fixUpLocation[2] = 0x40; + fixUpLocation[3] = 0x00; + } + break; + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a clear eax + fixUpLocation[-1] = 0x33; // xorl eax,eax + fixUpLocation[0] = 0xC0; + fixUpLocation[1] = 0x90; // 1-byte nop + fixUpLocation[2] = 0x90; // 1-byte nop + fixUpLocation[3] = 0x90; // 1-byte nop + } + break; + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + set32BE(fixUpLocation, 0x60000000); + } + break; + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a li r3,0 + set32BE(fixUpLocation, 0x38600000); + } + break; + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + set32LE(fixUpLocation, 0xE1A00000); + } + break; + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to 'eor r0, r0, r0' + set32LE(fixUpLocation, 0xE0200000); + } + break; + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change 32-bit blx call site to two thumb NOPs + set32LE(fixUpLocation, 0x46C046C0); + } + break; + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change 32-bit blx call site to 'nop', 'eor r0, r0' + set32LE(fixUpLocation, 0x46C04040); + } + break; + case ld::Fixup::kindLazyTarget: + break; + case ld::Fixup::kindSetLazyOffset: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + accumulator = this->lazyBindingInfoOffsetForLazyPointerAddress(fit->u.target->finalAddress()); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentAddendOnly ) + accumulator = 0; + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentAddendOnly ) + accumulator = 0; + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian32: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentAddendOnly ) + accumulator = 0; + set32BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian64: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentAddendOnly ) + accumulator = 0; + set64BE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: + accumulator = tlvTemplateOffsetOf(state, fit); + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: + accumulator = tlvTemplateOffsetOf(state, fit); + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + if ( fit->contentAddendOnly ) + delta = 0; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: + // TLV entry was optimized away, change movl instruction to a leal + if ( fixUpLocation[-1] != 0xA1 ) + throw "TLV load reloc does not point to a movl <abs-address>,<reg> instruction"; + fixUpLocation[-1] = 0xB8; + accumulator = addressOf(state, fit, &toTarget); + set32LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + // GOT entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "GOT load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + accumulator = addressOf(state, fit, &toTarget); + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: + // TLV entry was optimized away, change movq instruction to a leaq + if ( fixUpLocation[-2] != 0x8B ) + throw "TLV load reloc does not point to a movq instruction"; + fixUpLocation[-2] = 0x8D; + accumulator = addressOf(state, fit, &toTarget); + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckRIP32(delta, state, atom, fit); + set32LE(fixUpLocation, delta); + break; + case ld::Fixup::kindStoreTargetAddressARMBranch24: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + // fall into kindStoreARMBranch24 case + case ld::Fixup::kindStoreARMBranch24: + // The pc added will be +8 from the pc + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); + rangeCheckARMBranch24(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + // Make sure we are calling arm with bl, thumb with blx + is_bl = ((instruction & 0xFF000000) == 0xEB000000); + is_blx = ((instruction & 0xFE000000) == 0xFA000000); + if ( is_bl && thumbTarget ) { + uint32_t opcode = 0xFA000000; + uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; + uint32_t h_bit = (uint32_t)(delta << 23) & 0x01000000; + newInstruction = opcode | h_bit | disp; + } + else if ( is_blx && !thumbTarget ) { + uint32_t opcode = 0xEB000000; + uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; + newInstruction = opcode | disp; + } + else if ( !is_bl && !is_blx && thumbTarget ) { + throwf("don't know how to convert instruction %x referencing %s to thumb", + instruction, referenceTargetAtomName(state, fit)); + } + else { + newInstruction = (instruction & 0xFF000000) | ((uint32_t)(delta >> 2) & 0x00FFFFFF); + } + set32LE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + accumulator = addressOf(state, fit, &toTarget); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + // fall into kindStoreThumbBranch22 case + case ld::Fixup::kindStoreThumbBranch22: + instruction = get32LE(fixUpLocation); + is_bl = ((instruction & 0xD000F800) == 0xD000F000); + is_blx = ((instruction & 0xD000F800) == 0xC000F000); + is_b = ((instruction & 0xD000F800) == 0x9000F000); + // If the target is not thumb, we will be generating a blx instruction + // Since blx cannot have the low bit set, set bit[1] of the target to + // bit[1] of the base address, so that the difference is a multiple of + // 4 bytes. + if ( !thumbTarget ) { + accumulator &= -3ULL; + accumulator |= ((atom->finalAddress() + fit->offsetInAtom ) & 2LL); + } + // The pc added will be +4 from the pc + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4); + rangeCheckThumbBranch22(delta, state, atom, fit); + if ( _options.preferSubArchitecture() && _options.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t s = (uint32_t)(delta >> 24) & 0x1; + uint32_t i1 = (uint32_t)(delta >> 23) & 0x1; + uint32_t i2 = (uint32_t)(delta >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(delta >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(delta >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + if ( is_bl ) { + if ( thumbTarget ) + instruction = 0xD000F000; // keep bl + else + instruction = 0xC000F000; // change to blx + } + else if ( is_blx ) { + if ( thumbTarget ) + instruction = 0xD000F000; // change to bl + else + instruction = 0xC000F000; // keep blx + } + else if ( is_b ) { + if ( !thumbTarget ) + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, referenceTargetAtomName(state, fit)); + instruction = 0x9000F000; // keep b + } + else if ( is_b ) { + if ( !thumbTarget ) + throwf("don't know how to convert branch instruction %x referencing %s to bx", + instruction, referenceTargetAtomName(state, fit)); + instruction = 0x9000F000; // keep b + } + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + newInstruction = instruction | (nextDisp << 16) | firstDisp; + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, delta, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + set32LE(fixUpLocation, newInstruction); + } + else { + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t firstDisp = (uint32_t)(delta >> 12) & 0x7FF; + uint32_t nextDisp = (uint32_t)(delta >> 1) & 0x7FF; + if ( is_bl && !thumbTarget ) { + instruction = 0xE800F000; + } + else if ( is_blx && thumbTarget ) { + instruction = 0xF800F000; + } + else if ( !is_bl && !is_blx && !thumbTarget ) { + throwf("don't know how to convert instruction %x referencing %s to arm", + instruction, referenceTargetAtomName(state, fit)); + } + else { + instruction = instruction & 0xF800F800; + } + newInstruction = instruction | (nextDisp << 16) | firstDisp; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreARMLow16: + { + uint32_t imm4 = (accumulator & 0x0000F000) >> 12; + uint32_t imm12 = accumulator & 0x00000FFF; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreARMHigh16: + { + uint32_t imm4 = (accumulator & 0xF0000000) >> 28; + uint32_t imm12 = (accumulator & 0x0FFF0000) >> 16; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreThumbLow16: + { + uint32_t imm4 = (accumulator & 0x0000F000) >> 12; + uint32_t i = (accumulator & 0x00000800) >> 11; + uint32_t imm3 = (accumulator & 0x00000700) >> 8; + uint32_t imm8 = accumulator & 0x000000FF; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreThumbHigh16: + { + uint32_t imm4 = (accumulator & 0xF0000000) >> 28; + uint32_t i = (accumulator & 0x08000000) >> 27; + uint32_t imm3 = (accumulator & 0x07000000) >> 24; + uint32_t imm8 = (accumulator & 0x00FF0000) >> 16; + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + accumulator = addressOf(state, fit, &toTarget); + if ( fit->contentDetlaToAddendOnly ) + accumulator = 0; + // fall into kindStorePPCBranch24 case + case ld::Fixup::kindStorePPCBranch24: + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckPPCBranch24(delta, state, atom, fit); + instruction = get32BE(fixUpLocation); + newInstruction = (instruction & 0xFC000003) | ((uint32_t)delta & 0x03FFFFFC); + set32BE(fixUpLocation, newInstruction); + break; + } + } +} + +void OutputFile::copyNoOps(uint8_t* from, uint8_t* to) +{ + switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0x60000000); + break; + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + for (uint8_t* p=from; p < to; ++p) + *p = 0x90; + break; + case CPU_TYPE_ARM: + // fixme: need thumb nop? + for (uint8_t* p=from; p < to; p += 4) + OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000); + break; + default: + for (uint8_t* p=from; p < to; ++p) + *p = 0x00; + break; + } +} + +bool OutputFile::takesNoDiskSpace(const ld::Section* sect) +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typePageZero: + case ld::Section::typeStack: + case ld::Section::typeAbsoluteSymbols: + case ld::Section::typeTentativeDefs: + return true; + default: + break; + } + return false; +} + +bool OutputFile::hasZeroForFileOffset(const ld::Section* sect) +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typePageZero: + case ld::Section::typeStack: + case ld::Section::typeTentativeDefs: + return true; + default: + break; + } + return false; +} + + +void OutputFile::writeOutputFile(ld::Internal& state) +{ + // for UNIX conformance, error if file exists and is not writable + if ( (access(_options.outputFilePath(), F_OK) == 0) && (access(_options.outputFilePath(), W_OK) == -1) ) + throwf("can't write output file: %s", _options.outputFilePath()); + + int permissions = 0777; + if ( _options.outputKind() == Options::kObjectFile ) + permissions = 0666; + // Calling unlink first assures the file is gone so that open creates it with correct permissions + // It also handles the case where __options.outputFilePath() file is not writable but its directory is + // And it means we don't have to truncate the file when done writing (in case new is smaller than old) + // Lastly, only delete existing file if it is a normal file (e.g. not /dev/null). + struct stat stat_buf; + if ( (stat(_options.outputFilePath(), &stat_buf) != -1) && (stat_buf.st_mode & S_IFREG) ) + (void)unlink(_options.outputFilePath()); + + // try to allocate buffer for entire output file content + uint8_t* wholeBuffer = (uint8_t*)calloc(_fileSize, 1); + if ( wholeBuffer == NULL ) + throwf("can't create buffer of %llu bytes for output", _fileSize); + + if ( _options.UUIDMode() == Options::kUUIDRandom ) { + uint8_t bits[16]; + ::uuid_generate_random(bits); + _headersAndLoadCommandAtom->setUUID(bits); + } + + // have each atom write itself + uint64_t fileOffsetOfEndOfLastAtom = 0; + uint64_t mhAddress = 0; + bool lastAtomUsesNoOps = false; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeMachHeader ) + mhAddress = sect->address; + if ( takesNoDiskSpace(sect) ) + continue; + const bool sectionUsesNops = (sect->type() == ld::Section::typeCode); + //fprintf(stderr, "file offset=0x%08llX, section %s\n", sect->fileOffset, sect->sectionName()); + std::vector<const ld::Atom*>& atoms = sect->atoms; + for (std::vector<const ld::Atom*>::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->definition() == ld::Atom::definitionProxy ) + continue; + try { + uint64_t fileOffset = atom->finalAddress() - sect->address + sect->fileOffset; + // check for alignment padding between atoms + if ( (fileOffset != fileOffsetOfEndOfLastAtom) && lastAtomUsesNoOps ) { + this->copyNoOps(&wholeBuffer[fileOffsetOfEndOfLastAtom], &wholeBuffer[fileOffset]); + } + // copy atom content + atom->copyRawContent(&wholeBuffer[fileOffset]); + // apply fix ups + this->applyFixUps(state, mhAddress, atom, &wholeBuffer[fileOffset]); + fileOffsetOfEndOfLastAtom = fileOffset+atom->size(); + lastAtomUsesNoOps = sectionUsesNops; + } + catch (const char* msg) { + if ( atom->file() != NULL ) + throwf("%s in %s from %s", msg, atom->name(), atom->file()->path()); + else + throwf("%s in %s", msg, atom->name()); + } + } + } + + // compute UUID + if ( _options.UUIDMode() == Options::kUUIDContent ) { + const bool log = false; + if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + uint32_t stabsStringsOffsetStart; + uint32_t tabsStringsOffsetEnd; + uint32_t stabsOffsetStart; + uint32_t stabsOffsetEnd; + if ( _symbolTableAtom->hasStabs(stabsStringsOffsetStart, tabsStringsOffsetEnd, stabsOffsetStart, stabsOffsetEnd) ) { + // find two areas of file that are stabs info and should not contribute to checksum + uint64_t stringPoolFileOffset = 0; + uint64_t symbolTableFileOffset = 0; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeLinkEdit ) { + if ( strcmp(sect->sectionName(), "__string_pool") == 0 ) + stringPoolFileOffset = sect->fileOffset; + else if ( strcmp(sect->sectionName(), "__symbol_table") == 0 ) + symbolTableFileOffset = sect->fileOffset; + } + } + uint64_t firstStabNlistFileOffset = symbolTableFileOffset + stabsOffsetStart; + uint64_t lastStabNlistFileOffset = symbolTableFileOffset + stabsOffsetEnd; + uint64_t firstStabStringFileOffset = stringPoolFileOffset + stabsStringsOffsetStart; + uint64_t lastStabStringFileOffset = stringPoolFileOffset + tabsStringsOffsetEnd; + if ( log ) fprintf(stderr, "firstStabNlistFileOffset=0x%08llX\n", firstStabNlistFileOffset); + if ( log ) fprintf(stderr, "lastStabNlistFileOffset=0x%08llX\n", lastStabNlistFileOffset); + if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); + if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); + assert(firstStabNlistFileOffset <= firstStabStringFileOffset); + + CC_MD5_CTX md5state; + CC_MD5_Init(&md5state); + // checksum everything up to first stabs nlist + if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); + // checkusm everything after last stabs nlist and up to first stabs string + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); + // checksum everything after last stabs string to end of file + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); + CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); + CC_MD5_Final(digest, &md5state); + if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], + digest[3], digest[4], digest[5], digest[6], digest[7]); + } + else { + CC_MD5(wholeBuffer, _fileSize, digest); + } + // <rdar://problem/6723729> LC_UUID uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats + digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 ); + digest[8] = ( digest[8] & 0x3F ) | 0x80; + // update buffer with new UUID + _headersAndLoadCommandAtom->setUUID(digest); + _headersAndLoadCommandAtom->recopyUUIDCommand(); + } + } + + // write whole output file in one chunk + int fd = open(_options.outputFilePath(), O_CREAT | O_WRONLY | O_TRUNC, permissions); + if ( fd == -1 ) + throwf("can't open output file for writing: %s, errno=%d", _options.outputFilePath(), errno); + if ( ::pwrite(fd, wholeBuffer, _fileSize, 0) == -1 ) + throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); + close(fd); + free(wholeBuffer); +} + +struct AtomByNameSorter +{ + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } +}; + +void OutputFile::buildSymbolTable(ld::Internal& state) +{ + unsigned int machoSectionIndex = 0; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + bool setMachoSectionIndex = !sect->isSectionHidden() && (sect->type() != ld::Section::typeTentativeDefs); + if ( setMachoSectionIndex ) + ++machoSectionIndex; + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( setMachoSectionIndex ) + (const_cast<ld::Atom*>(atom))->setMachoSection(machoSectionIndex); + else if ( sect->type() == ld::Section::typeMachHeader ) + (const_cast<ld::Atom*>(atom))->setMachoSection(1); // __mh_execute_header is not in any section by needs n_sect==1 + else if ( sect->type() == ld::Section::typeLastSection ) + (const_cast<ld::Atom*>(atom))->setMachoSection(machoSectionIndex); // use section index of previous section + else if ( sect->type() == ld::Section::typeFirstSection ) + (const_cast<ld::Atom*>(atom))->setMachoSection(machoSectionIndex+1); // use section index of next section + + // in -r mode, clarify symbolTableNotInFinalLinkedImages + if ( _options.outputKind() == Options::kObjectFile ) { + if ( _options.architecture() == CPU_TYPE_X86_64 ) { + // x86_64 .o files need labels on anonymous literal strings + if ( (sect->type() == ld::Section::typeCString) && (atom->combine() == ld::Atom::combineByNameAndContent) ) { + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + _localAtoms.push_back(atom); + continue; + } + } + if ( sect->type() == ld::Section::typeCFI ) { + if ( _options.removeEHLabels() ) + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); + else + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + } + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + } + + // TEMP work around until <rdar://problem/7702923> goes in + if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) + && (atom->scope() == ld::Atom::scopeLinkageUnit) + && (_options.outputKind() == Options::kDynamicLibrary) ) { + (const_cast<ld::Atom*>(atom))->setScope(ld::Atom::scopeGlobal); + } + + // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden + if ( atom->autoHide() && (_options.outputKind() != Options::kObjectFile) ) { + // adding auto-hide symbol to .exp file should keep it global + if ( !_options.hasExportMaskList() || !_options.shouldExport(atom->name()) ) + (const_cast<ld::Atom*>(atom))->setScope(ld::Atom::scopeLinkageUnit); + } + + // <rdar://problem/8626058> ld should consistently warn when resolvers are not exported + if ( (atom->contentType() == ld::Atom::typeResolver) && (atom->scope() == ld::Atom::scopeLinkageUnit) ) + warning("resolver functions should be external, but '%s' is hidden", atom->name()); + + if ( sect->type() == ld::Section::typeImportProxies ) { + if ( atom->combine() == ld::Atom::combineByName ) + this->usesWeakExternalSymbols = true; + // alias proxy is a re-export with a name change, don't import changed name + if ( ! atom->isAlias() ) + _importedAtoms.push_back(atom); + // scope of proxies are usually linkage unit, so done + // if scope is global, we need to re-export it too + if ( atom->scope() == ld::Atom::scopeGlobal ) + _exportedAtoms.push_back(atom); + continue; + } + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) { + assert(_options.outputKind() != Options::kObjectFile); + continue; // don't add to symbol table + } + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotIn ) { + continue; // don't add to symbol table + } + + if ( (atom->definition() == ld::Atom::definitionTentative) && (_options.outputKind() == Options::kObjectFile) ) { + if ( _options.makeTentativeDefinitionsReal() ) { + // -r -d turns tentative defintions into real def + _exportedAtoms.push_back(atom); + } + else { + // in mach-o object files tentative defintions are stored like undefined symbols + _importedAtoms.push_back(atom); + } + continue; + } + + // <rdar://problem/7977374> Add command line options to control symbol weak-def bit on exported symbols + if ( _options.hasWeakBitTweaks() && (atom->definition() == ld::Atom::definitionRegular) ) { + const char* name = atom->name(); + if ( atom->scope() == ld::Atom::scopeGlobal ) { + if ( atom->combine() == ld::Atom::combineNever ) { + if ( _options.forceWeak(name) ) + (const_cast<ld::Atom*>(atom))->setCombine(ld::Atom::combineByName); + } + else if ( atom->combine() == ld::Atom::combineByName ) { + if ( _options.forceNotWeak(name) ) + (const_cast<ld::Atom*>(atom))->setCombine(ld::Atom::combineNever); + } + } + else { + if ( _options.forceWeakNonWildCard(name) ) + warning("cannot force to be weak, non-external symbol %s", name); + else if ( _options.forceNotWeakNonWildcard(name) ) + warning("cannot force to be not-weak, non-external symbol %s", name); + } + } + + switch ( atom->scope() ) { + case ld::Atom::scopeTranslationUnit: + if ( _options.keepLocalSymbol(atom->name()) ) { + _localAtoms.push_back(atom); + } + else { + if ( _options.outputKind() == Options::kObjectFile ) { + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableInWithRandomAutoStripLabel); + _localAtoms.push_back(atom); + } + else + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); + } + break; + case ld::Atom::scopeGlobal: + _exportedAtoms.push_back(atom); + break; + case ld::Atom::scopeLinkageUnit: + if ( _options.outputKind() == Options::kObjectFile ) { + if ( _options.keepPrivateExterns() ) { + assert( (atom->combine() == ld::Atom::combineNever) || (atom->combine() == ld::Atom::combineByName) ); + _exportedAtoms.push_back(atom); + } + else if ( _options.keepLocalSymbol(atom->name()) ) { + _localAtoms.push_back(atom); + } + else { + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableInWithRandomAutoStripLabel); + _localAtoms.push_back(atom); + } + } + else { + if ( _options.keepLocalSymbol(atom->name()) ) + _localAtoms.push_back(atom); + else + (const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn); + } + break; + } + } + } + + // sort by name + std::sort(_exportedAtoms.begin(), _exportedAtoms.end(), AtomByNameSorter()); + std::sort(_importedAtoms.begin(), _importedAtoms.end(), AtomByNameSorter()); + +} + +void OutputFile::addPreloadLinkEdit(ld::Internal& state) +{ + switch ( _options.architecture() ) { + case CPU_TYPE_I386: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<x86>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<x86>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<x86>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom<x86>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_X86_64: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<x86_64>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<x86_64>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<x86_64>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom<x86_64>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_ARM: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<arm>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<arm>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<arm>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom<arm>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + default: + throw "architecture not supported for -preload"; + } + +} + + +void OutputFile::addLinkEdit(ld::Internal& state) +{ + // for historical reasons, -preload orders LINKEDIT content differently + if ( _options.outputKind() == Options::kPreload ) + return addPreloadLinkEdit(state); + + switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom<ppc>(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom<ppc>(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom<ppc>(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom<ppc>(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom<ppc>(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom<ppc>(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<ppc>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom<ppc>(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom<ppc>(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom<ppc>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<ppc>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<ppc>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_I386: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom<x86>(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom<x86>(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom<x86>(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom<x86>(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom<x86>(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom<x86>(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<x86>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom<x86>(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom<x86>(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom<x86>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<x86>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<x86>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_X86_64: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom<x86_64>(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom<x86_64>(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom<x86_64>(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom<x86_64>(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom<x86_64>(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom<x86_64>(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<x86_64>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom<x86_64>(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom<x86_64>(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom<x86_64>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<x86_64>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<x86_64>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 8); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_ARM: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom<arm>(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom<arm>(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom<arm>(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom<arm>(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom<arm>(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom<arm>(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<arm>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom<arm>(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom<arm>(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom<arm>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<arm>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<arm>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + case CPU_TYPE_POWERPC64: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom<ppc64>(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom<ppc64>(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom<ppc64>(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom<ppc64>(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom<ppc64>(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom<ppc64>(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom<ppc64>(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom<ppc64>(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom<ppc64>(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom<ppc64>(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom<ppc64>(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom<ppc64>(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; + default: + throw "unknown architecture"; + } +} + +void OutputFile::addLoadCommands(ld::Internal& state) +{ + switch ( _options.architecture() ) { + case CPU_TYPE_X86_64: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<x86_64>(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_ARM: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<arm>(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_I386: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<x86>(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_POWERPC: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<ppc>(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + case CPU_TYPE_POWERPC64: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom<ppc64>(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; + default: + throw "unknown architecture"; + } +} + +uint32_t OutputFile::dylibCount() +{ + return _dylibsToLoad.size(); +} + +const ld::dylib::File* OutputFile::dylibByOrdinal(unsigned int ordinal) +{ + assert( ordinal > 0 ); + assert( ordinal <= _dylibsToLoad.size() ); + return _dylibsToLoad[ordinal-1]; +} + +bool OutputFile::hasOrdinalForInstallPath(const char* path, int* ordinal) +{ + for (std::map<const ld::dylib::File*, int>::const_iterator it = _dylibToOrdinal.begin(); it != _dylibToOrdinal.end(); ++it) { + const char* installPath = it->first->installPath(); + if ( (installPath != NULL) && (strcmp(path, installPath) == 0) ) { + *ordinal = it->second; + return true; + } + } + return false; +} + +uint32_t OutputFile::dylibToOrdinal(const ld::dylib::File* dylib) +{ + return _dylibToOrdinal[dylib]; +} + + +void OutputFile::buildDylibOrdinalMapping(ld::Internal& state) +{ + // count non-public re-exported dylibs + unsigned int nonPublicReExportCount = 0; + for (std::vector<ld::dylib::File*>::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + if ( aDylib->willBeReExported() && ! aDylib->hasPublicInstallName() ) + ++nonPublicReExportCount; + } + + // look at each dylib supplied in state + bool hasReExports = false; + bool haveLazyDylibs = false; + for (std::vector<ld::dylib::File*>::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + int ordinal; + if ( aDylib == state.bundleLoader ) { + _dylibToOrdinal[aDylib] = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; + } + else if ( this->hasOrdinalForInstallPath(aDylib->installPath(), &ordinal) ) { + // already have a dylib with that install path, map all uses to that ordinal + _dylibToOrdinal[aDylib] = ordinal; + } + else if ( aDylib->willBeLazyLoadedDylib() ) { + // all lazy dylib need to be at end of ordinals + haveLazyDylibs = true; + } + else if ( aDylib->willBeReExported() && ! aDylib->hasPublicInstallName() && (nonPublicReExportCount >= 2) ) { + _dylibsToLoad.push_back(aDylib); + _dylibToOrdinal[aDylib] = BIND_SPECIAL_DYLIB_SELF; + } + else { + // first time this install path seen, create new ordinal + _dylibsToLoad.push_back(aDylib); + _dylibToOrdinal[aDylib] = _dylibsToLoad.size(); + } + if ( aDylib->explicitlyLinked() && aDylib->willBeReExported() ) + hasReExports = true; + } + if ( haveLazyDylibs ) { + // second pass to determine ordinals for lazy loaded dylibs + for (std::vector<ld::dylib::File*>::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + if ( aDylib->willBeLazyLoadedDylib() ) { + int ordinal; + if ( this->hasOrdinalForInstallPath(aDylib->installPath(), &ordinal) ) { + // already have a dylib with that install path, map all uses to that ordinal + _dylibToOrdinal[aDylib] = ordinal; + } + else { + // first time this install path seen, create new ordinal + _dylibsToLoad.push_back(aDylib); + _dylibToOrdinal[aDylib] = _dylibsToLoad.size(); + } + } + } + } + _noReExportedDylibs = !hasReExports; + //fprintf(stderr, "dylibs:\n"); + //for (std::map<const ld::dylib::File*, int>::const_iterator it = _dylibToOrdinal.begin(); it != _dylibToOrdinal.end(); ++it) { + // fprintf(stderr, " %p ord=%u, install_name=%s\n",it->first, it->second, it->first->installPath()); + //} +} + +uint32_t OutputFile::lazyBindingInfoOffsetForLazyPointerAddress(uint64_t lpAddress) +{ + return _lazyPointerAddressToInfoOffset[lpAddress]; +} + +void OutputFile::setLazyBindingInfoOffset(uint64_t lpAddress, uint32_t lpInfoOffset) +{ + _lazyPointerAddressToInfoOffset[lpAddress] = lpInfoOffset; +} + +int OutputFile::compressedOrdinalForAtom(const ld::Atom* target) +{ + // flat namespace images use zero for all ordinals + if ( _options.nameSpace() != Options::kTwoLevelNameSpace ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + // handle -interposable + if ( target->definition() == ld::Atom::definitionRegular ) + return BIND_SPECIAL_DYLIB_SELF; + + // regular ordinal + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file()); + if ( dylib != NULL ) + return _dylibToOrdinal[dylib]; + + // handle undefined dynamic_lookup + if ( _options.undefinedTreatment() == Options::kUndefinedDynamicLookup ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + // handle -U _foo + if ( _options.allowedUndefined(target->name()) ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + throw "can't find ordinal for imported symbol"; +} + + +bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindStoreX86BranchPCRel8: + case ld::Fixup::kindStoreX86BranchPCRel32: + case ld::Fixup::kindStoreX86PCRel8: + case ld::Fixup::kindStoreX86PCRel16: + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreARMLoad12: + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStorePPCBranch14: + case ld::Fixup::kindStorePPCPicLow14: + case ld::Fixup::kindStorePPCPicLow16: + case ld::Fixup::kindStorePPCPicHigh16AddLow: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMLoad12: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + return true; + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + return (_options.outputKind() != Options::kKextBundle); + default: + break; + } + return false; +} + +bool OutputFile::isStore(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindNone: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindSubtractTargetAddress: + case ld::Fixup::kindAddAddend: + case ld::Fixup::kindSubtractAddend: + case ld::Fixup::kindSetTargetImageOffset: + case ld::Fixup::kindSetTargetSectionOffset: + return false; + default: + break; + } + return true; +} + + +bool OutputFile::setsTarget(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindLazyTarget: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMLoad12: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + return true; + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + return (_options.outputKind() == Options::kObjectFile); + default: + break; + } + return false; +} + +bool OutputFile::isPointerToTarget(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + case ld::Fixup::kindLazyTarget: + return true; + default: + break; + } + return false; +} +bool OutputFile::isPointerFromTarget(ld::Fixup::Kind kind) +{ + switch ( kind ) { + case ld::Fixup::kindSubtractTargetAddress: + return true; + default: + break; + } + return false; +} + + +uint64_t OutputFile::lookBackAddend(ld::Fixup::iterator fit) +{ + uint64_t addend = 0; + switch ( fit->clusterSize ) { + case ld::Fixup::k1of1: + case ld::Fixup::k1of2: + case ld::Fixup::k2of2: + break; + case ld::Fixup::k2of3: + --fit; + switch ( fit->kind ) { + case ld::Fixup::kindAddAddend: + addend += fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + addend -= fit->u.addend; + break; + default: + throw "unexpected fixup kind for binding"; + } + break; + case ld::Fixup::k1of3: + ++fit; + switch ( fit->kind ) { + case ld::Fixup::kindAddAddend: + addend += fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + addend -= fit->u.addend; + break; + default: + throw "unexpected fixup kind for binding"; + } + break; + default: + throw "unexpected fixup cluster size for binding"; + } + return addend; +} + + + + + +void OutputFile::generateLinkEditInfo(ld::Internal& state) +{ + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + bool objc1ClassRefSection = ( (sect->type() == ld::Section::typeCStringPointer) + && (strcmp(sect->sectionName(), "__cls_refs") == 0) + && (strcmp(sect->segmentName(), "__OBJC") == 0) ); + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + + // Record regular atoms that override a dylib's weak definitions + if ( (atom->scope() == ld::Atom::scopeGlobal) && atom->overridesDylibsWeakDef() ) { + if ( _options.makeCompressedDyldInfo() ) { + uint8_t wtype = BIND_TYPE_OVERRIDE_OF_WEAKDEF_IN_DYLIB; + bool nonWeakDef = (atom->combine() == ld::Atom::combineNever); + _weakBindingInfo.push_back(BindingInfo(wtype, atom->name(), nonWeakDef, atom->finalAddress(), 0)); + } + this->overridesWeakExternalSymbols = true; + if ( _options.warnWeakExports() ) + warning("overrides weak external symbol: %s", atom->name()); + } + + ld::Fixup* fixupWithTarget = NULL; + ld::Fixup* fixupWithMinusTarget = NULL; + ld::Fixup* fixupWithStore = NULL; + const ld::Atom* target = NULL; + const ld::Atom* minusTarget = NULL; + uint64_t targetAddend = 0; + uint64_t minusTargetAddend = 0; + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->firstInCluster() ) { + fixupWithTarget = NULL; + fixupWithMinusTarget = NULL; + fixupWithStore = NULL; + target = NULL; + minusTarget = NULL; + targetAddend = 0; + minusTargetAddend = 0; + } + if ( this->setsTarget(fit->kind) ) { + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + fixupWithTarget = fit; + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + fixupWithTarget = fit; + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + assert(target != NULL); + } + switch ( fit->kind ) { + case ld::Fixup::kindAddAddend: + targetAddend = fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + minusTargetAddend = fit->u.addend; + break; + case ld::Fixup::kindSubtractTargetAddress: + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + fixupWithMinusTarget = fit; + minusTarget = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + fixupWithMinusTarget = fit; + minusTarget = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + assert(minusTarget != NULL); + break; + default: + break; + } + if ( this->isStore(fit->kind) ) { + fixupWithStore = fit; + } + if ( fit->lastInCluster() ) { + if ( (fixupWithStore != NULL) && (target != NULL) ) { + if ( _options.outputKind() == Options::kObjectFile ) { + this->addSectionRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + target, minusTarget, targetAddend, minusTargetAddend); + } + else { + if ( _options.makeCompressedDyldInfo() ) { + this->addDyldInfo(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + target, minusTarget, targetAddend, minusTargetAddend); + } + else { + this->addClassicRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + target, minusTarget, targetAddend, minusTargetAddend); + } + } + } + else if ( objc1ClassRefSection && (target != NULL) && (fixupWithStore == NULL) ) { + // check for class refs to lazy loaded dylibs + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal class reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + } + } + } + } + } +} + + +void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) +{ + if ( (atom->contentType() == ld::Atom::typeStub) || (atom->contentType() == ld::Atom::typeStubHelper) ) { + // silently let stubs (synthesized by linker) use text relocs + } + else if ( _options.allowTextRelocs() ) { + if ( _options.warnAboutTextRelocs() ) + warning("text reloc in %s to %s", atom->name(), target->name()); + } + else if ( _options.positionIndependentExecutable() && (_options.iphoneOSVersionMin() >= ld::iPhone4_3) ) { + if ( ! this->pieDisabled ) { + warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " + "but used in %s from %s. " + "To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie", + atom->name(), atom->file()->path()); + } + this->pieDisabled = true; + } + else { + throwf("illegal text reloc to %s from %s in %s", target->name(), target->file()->path(), atom->name()); + } +} + +void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend) +{ + if ( sect->isSectionHidden() ) + return; + + // no need to rebase or bind PCRel stores + if ( this->isPcRelStore(fixupWithStore->kind) ) { + // as long as target is in same linkage unit + if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) + return; + } + + // no need to rebase or bind PIC internal pointer diff + if ( minusTarget != NULL ) { + // with pointer diffs, both need to be in same linkage unit + assert(minusTarget->definition() != ld::Atom::definitionProxy); + assert(target != NULL); + assert(target->definition() != ld::Atom::definitionProxy); + if ( target == minusTarget ) { + // This is a compile time constant and could have been optimized away by compiler + return; + } + + // make sure target is not global and weak + if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) + && (atom->section().type() != ld::Section::typeCFI) + && (atom->section().type() != ld::Section::typeDtraceDOF) + && (atom->section().type() != ld::Section::typeUnwindInfo) ) { + // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols + throwf("bad codegen, pointer diff in %s to global weak symbol %s", atom->name(), target->name()); + } + return; + } + + // no need to rebase or bind an atom's references to itself if the output is not slidable + if ( (atom == target) && !_options.outputSlidable() ) + return; + + // cluster has no target, so needs no rebasing or binding + if ( target == NULL ) + return; + + bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 ); + bool needsRebase = false; + bool needsBinding = false; + bool needsLazyBinding = false; + bool needsWeakBinding = false; + + uint8_t rebaseType = REBASE_TYPE_POINTER; + uint8_t type = BIND_TYPE_POINTER; + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file()); + bool weak_import = ((dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked())); + uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom; + uint64_t addend = targetAddend - minusTargetAddend; + + // special case lazy pointers + if ( fixupWithTarget->kind == ld::Fixup::kindLazyTarget ) { + assert(fixupWithTarget->u.target == target); + assert(addend == 0); + // lazy dylib lazy pointers do not have any dyld info + if ( atom->section().type() == ld::Section::typeLazyDylibPointer ) + return; + // lazy binding to weak definitions are done differently + // they are directly bound to target, then have a weak bind in case of a collision + if ( target->combine() == ld::Atom::combineByName ) { + if ( target->definition() == ld::Atom::definitionProxy ) { + // weak def exported from another dylib + // must non-lazy bind to it plus have weak binding info in case of collision + needsBinding = true; + needsWeakBinding = true; + } + else { + // weak def in this linkage unit. + // just rebase, plus have weak binding info in case of collision + // this will be done by other cluster on lazy pointer atom + } + } + else if ( (target->contentType() == ld::Atom::typeResolver) && (target->scope() != ld::Atom::scopeGlobal) ) { + // <rdar://problem/8553647> Hidden resolver functions should not have lazy binding info + needsLazyBinding = false; + } + else { + // normal case of a pointer to non-weak-def symbol, so can lazily bind + needsLazyBinding = true; + } + } + else { + // everything except lazy pointers + switch ( target->definition() ) { + case ld::Atom::definitionProxy: + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal data reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + if ( target->contentType() == ld::Atom::typeTLV ) { + if ( sect->type() != ld::Section::typeTLVPointers ) + throwf("illegal data reference in %s to thread local variable %s in dylib %s", + atom->name(), target->name(), dylib->path()); + } + if ( inReadOnlySeg ) + type = BIND_TYPE_TEXT_ABSOLUTE32; + needsBinding = true; + if ( target->combine() == ld::Atom::combineByName ) + needsWeakBinding = true; + break; + case ld::Atom::definitionRegular: + case ld::Atom::definitionTentative: + // only slideable images need rebasing info + if ( _options.outputSlidable() ) { + needsRebase = true; + } + // references to internal symbol never need binding + if ( target->scope() != ld::Atom::scopeGlobal ) + break; + // reference to global weak def needs weak binding + if ( (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) + needsWeakBinding = true; + else if ( _options.outputKind() == Options::kDynamicExecutable ) { + // in main executables, the only way regular symbols are indirected is if -interposable is used + if ( _options.interposable(target->name()) ) { + needsRebase = false; + needsBinding = true; + } + } + else { + // for flat-namespace or interposable two-level-namespace + // all references to exported symbols get indirected + if ( (_options.nameSpace() != Options::kTwoLevelNameSpace) || _options.interposable(target->name()) ) { + // <rdar://problem/5254468> no external relocs for flat objc classes + if ( strncmp(target->name(), ".objc_class_", 12) == 0 ) + break; + // no rebase info for references to global symbols that will have binding info + needsRebase = false; + needsBinding = true; + } + } + break; + case ld::Atom::definitionAbsolute: + break; + } + } + + // record dyld info for this cluster + if ( needsRebase ) { + if ( inReadOnlySeg ) { + noteTextReloc(atom, target); + sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment + rebaseType = REBASE_TYPE_TEXT_ABSOLUTE32; + } + _rebaseInfo.push_back(RebaseInfo(rebaseType, address)); + } + if ( needsBinding ) { + if ( inReadOnlySeg ) { + noteTextReloc(atom, target); + sect->hasExternalRelocs = true; // so dyld knows to change permissions on __TEXT segment + } + _bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend)); + } + if ( needsLazyBinding ) { + if ( _options.bindAtLoad() ) + _bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend)); + else + _lazyBindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend)); + } + if ( needsWeakBinding ) + _weakBindingInfo.push_back(BindingInfo(type, 0, target->name(), false, address, addend)); + + // record if weak imported + if ( weak_import && (target->definition() == ld::Atom::definitionProxy) ) + (const_cast<ld::Atom*>(target))->setWeakImported(); +} + + +void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend) +{ + if ( sect->isSectionHidden() ) + return; + + // non-lazy-pointer section is encoded in indirect symbol table - not using relocations + if ( (sect->type() == ld::Section::typeNonLazyPointer) && (_options.outputKind() != Options::kKextBundle) ) { + assert(target != NULL); + assert(fixupWithTarget != NULL); + // record if weak imported + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file()); + if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) + (const_cast<ld::Atom*>(target))->setWeakImported(); + return; + } + + // no need to rebase or bind PCRel stores + if ( this->isPcRelStore(fixupWithStore->kind) ) { + // as long as target is in same linkage unit + if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) + return; + } + + // no need to rebase or bind PIC internal pointer diff + if ( minusTarget != NULL ) { + // with pointer diffs, both need to be in same linkage unit + assert(minusTarget->definition() != ld::Atom::definitionProxy); + assert(target != NULL); + assert(target->definition() != ld::Atom::definitionProxy); + // make sure target is not global and weak + if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) + && (atom->section().type() != ld::Section::typeCFI) + && (atom->section().type() != ld::Section::typeDtraceDOF) + && (atom->section().type() != ld::Section::typeUnwindInfo) + && (minusTarget != target) ) { + // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols + throwf("bad codegen, pointer diff in %s to global weak symbol %s", atom->name(), target->name()); + } + return; + } + + // cluster has no target, so needs no rebasing or binding + if ( target == NULL ) + return; + + assert(_localRelocsAtom != NULL); + uint64_t relocAddress = atom->finalAddress() + fixupWithTarget->offsetInAtom - _localRelocsAtom->relocBaseAddress(state); + + bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 ); + bool needsLocalReloc = false; + bool needsExternReloc = false; + + switch ( fixupWithStore->kind ) { + case ld::Fixup::kindLazyTarget: + { + // lazy pointers don't need relocs, but might need weak_import bit set + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file()); + if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) + (const_cast<ld::Atom*>(target))->setWeakImported(); + } + break; + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreBigEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + // is pointer + switch ( target->definition() ) { + case ld::Atom::definitionProxy: + needsExternReloc = true; + break; + case ld::Atom::definitionRegular: + case ld::Atom::definitionTentative: + // only slideable images need local relocs + if ( _options.outputSlidable() ) + needsLocalReloc = true; + // references to internal symbol never need binding + if ( target->scope() != ld::Atom::scopeGlobal ) + break; + // reference to global weak def needs weak binding in dynamic images + if ( (target->combine() == ld::Atom::combineByName) + && (target->definition() == ld::Atom::definitionRegular) + && (_options.outputKind() != Options::kStaticExecutable) ) { + needsExternReloc = true; + } + else if ( _options.outputKind() == Options::kDynamicExecutable ) { + // in main executables, the only way regular symbols are indirected is if -interposable is used + if ( _options.interposable(target->name()) ) + needsExternReloc = true; + } + else { + // for flat-namespace or interposable two-level-namespace + // all references to exported symbols get indirected + if ( (_options.nameSpace() != Options::kTwoLevelNameSpace) || _options.interposable(target->name()) ) { + // <rdar://problem/5254468> no external relocs for flat objc classes + if ( strncmp(target->name(), ".objc_class_", 12) == 0 ) + break; + // no rebase info for references to global symbols that will have binding info + needsExternReloc = true; + } + } + if ( needsExternReloc ) + needsLocalReloc = false; + break; + case ld::Atom::definitionAbsolute: + break; + } + if ( needsExternReloc ) { + if ( inReadOnlySeg ) + noteTextReloc(atom, target); + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + throwf("illegal data reference to %s in lazy loaded dylib %s", target->name(), dylib->path()); + _externalRelocsAtom->addExternalPointerReloc(relocAddress, target); + sect->hasExternalRelocs = true; + fixupWithTarget->contentAddendOnly = true; + // record if weak imported + if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) ) + (const_cast<ld::Atom*>(target))->setWeakImported(); + } + else if ( needsLocalReloc ) { + assert(target != NULL); + if ( inReadOnlySeg ) + noteTextReloc(atom, target); + _localRelocsAtom->addPointerReloc(relocAddress, target->machoSection()); + sect->hasLocalRelocs = true; + } + break; + case ld::Fixup::kindStorePPCAbsLow14: + case ld::Fixup::kindStorePPCAbsLow16: + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + case ld::Fixup::kindStorePPCAbsHigh16: + { + assert(target != NULL); + if ( target->definition() == ld::Atom::definitionProxy ) + throwf("half word text relocs not supported in %s", atom->name()); + if ( _options.outputSlidable() ) { + if ( inReadOnlySeg ) + noteTextReloc(atom, target); + uint32_t machoSectionIndex = (target->definition() == ld::Atom::definitionAbsolute) + ? R_ABS : target->machoSection(); + _localRelocsAtom->addTextReloc(relocAddress, fixupWithTarget->kind, + target->finalAddress(), machoSectionIndex); + sect->hasLocalRelocs = true; + } + } + break; + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + if ( _options.outputKind() == Options::kKextBundle ) { + assert(target != NULL); + if ( target->definition() == ld::Atom::definitionProxy ) { + _externalRelocsAtom->addExternalCallSiteReloc(relocAddress, target); + fixupWithStore->contentAddendOnly = true; + } + } + break; + default: + break; + } +} + + +bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget) +{ + if ( _options.architecture() == CPU_TYPE_X86_64 ) { + // x86_64 uses external relocations for everthing that has a symbol + return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn ); + } + // most architectures use external relocations only for references + // to a symbol in another translation unit or for references to "weak symbols" or tentative definitions + assert(target != NULL); + if ( target->definition() == ld::Atom::definitionProxy ) + return true; + if ( (target->definition() == ld::Atom::definitionTentative) && ! _options.makeTentativeDefinitionsReal() ) + return true; + if ( target->scope() != ld::Atom::scopeGlobal ) + return false; + if ( (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) + return true; + return false; +} + + + + +void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend) +{ + if ( sect->isSectionHidden() ) + return; + + // in -r mode where there will be no labels on __eh_frame section, there is no need for relocations + if ( (sect->type() == ld::Section::typeCFI) && _options.removeEHLabels() ) + return; + + // non-lazy-pointer section is encoded in indirect symbol table - not using relocations + if ( sect->type() == ld::Section::typeNonLazyPointer ) + return; + + // tentative defs don't have any relocations + if ( sect->type() == ld::Section::typeTentativeDefs ) + return; + + assert(target != NULL); + assert(fixupWithTarget != NULL); + bool targetUsesExternalReloc = this->useExternalSectionReloc(atom, target, fixupWithTarget); + bool minusTargetUsesExternalReloc = (minusTarget != NULL) && this->useExternalSectionReloc(atom, minusTarget, fixupWithMinusTarget); + + // in x86_64 .o files an external reloc means the content contains just the addend + if ( _options.architecture() == CPU_TYPE_X86_64 ) { + if ( targetUsesExternalReloc ) { + fixupWithTarget->contentAddendOnly = true; + fixupWithStore->contentAddendOnly = true; + } + if ( minusTargetUsesExternalReloc ) + fixupWithMinusTarget->contentAddendOnly = true; + } + else { + // for other archs, content is addend only with (non pc-rel) pointers + // pc-rel instructions are funny. If the target is _foo+8 and _foo is + // external, then the pc-rel instruction *evalutates* to the address 8. + if ( targetUsesExternalReloc ) { + if ( isPcRelStore(fixupWithStore->kind) ) { + fixupWithTarget->contentDetlaToAddendOnly = true; + fixupWithStore->contentDetlaToAddendOnly = true; + } + else if ( minusTarget == NULL ){ + fixupWithTarget->contentAddendOnly = true; + fixupWithStore->contentAddendOnly = true; + } + } + } + + if ( fixupWithStore != NULL ) { + _sectionsRelocationsAtom->addSectionReloc(sect, fixupWithStore->kind, atom, fixupWithStore->offsetInAtom, + targetUsesExternalReloc, minusTargetUsesExternalReloc, + target, targetAddend, minusTarget, minusTargetAddend); + } + +} + + +void OutputFile::makeSplitSegInfo(ld::Internal& state) +{ + if ( !_options.sharedRegionEligible() ) + return; + + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + if ( strcmp(sect->segmentName(), "__TEXT") != 0 ) + continue; + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::Atom* target = NULL; + bool hadSubtract = false; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) + target = NULL; + if ( fit->kind == ld::Fixup::kindSubtractTargetAddress ) { + hadSubtract = true; + continue; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + switch ( fit->kind ) { + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + // if no subtract, then this is an absolute pointer which means + // there is also a text reloc which update_dyld_shared_cache will use. + if ( ! hadSubtract ) + break; + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStorePPCPicHigh16AddLow: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + assert(target != NULL); + if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { + _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind)); + } + break; + default: + break; + } + } + } + } +} + + +void OutputFile::writeMapFile(ld::Internal& state) +{ + if ( _options.generatedMapPath() != NULL ) { + FILE* mapFile = fopen(_options.generatedMapPath(), "w"); + if ( mapFile != NULL ) { + // write output path + fprintf(mapFile, "# Path: %s\n", _options.outputFilePath()); + // write output architecure + fprintf(mapFile, "# Arch: %s\n", _options.architectureName()); + // write UUID + //if ( fUUIDAtom != NULL ) { + // const uint8_t* uuid = fUUIDAtom->getUUID(); + // fprintf(mapFile, "# UUID: %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X \n", + // uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], + // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + //} + // write table of object files + std::map<const ld::File*, uint32_t> readerToOrdinal; + std::map<uint32_t, const ld::File*> ordinalToReader; + std::map<const ld::File*, uint32_t> readerToFileOrdinal; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::File* reader = atom->file(); + if ( reader == NULL ) + continue; + uint32_t readerOrdinal = reader->ordinal(); + std::map<const ld::File*, uint32_t>::iterator pos = readerToOrdinal.find(reader); + if ( pos == readerToOrdinal.end() ) { + readerToOrdinal[reader] = readerOrdinal; + ordinalToReader[readerOrdinal] = reader; + } + } + } + fprintf(mapFile, "# Object files:\n"); + fprintf(mapFile, "[%3u] %s\n", 0, "linker synthesized"); + uint32_t fileIndex = 0; + readerToFileOrdinal[NULL] = fileIndex++; + for(std::map<uint32_t, const ld::File*>::iterator it = ordinalToReader.begin(); it != ordinalToReader.end(); ++it) { + if ( it->first != 0 ) { + fprintf(mapFile, "[%3u] %s\n", fileIndex, it->second->path()); + readerToFileOrdinal[it->second] = fileIndex++; + } + } + // write table of sections + fprintf(mapFile, "# Sections:\n"); + fprintf(mapFile, "# Address\tSize \tSegment\tSection\n"); + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + fprintf(mapFile, "0x%08llX\t0x%08llX\t%s\t%s\n", sect->address, sect->size, + sect->segmentName(), sect->sectionName()); + } + // write table of symbols + fprintf(mapFile, "# Symbols:\n"); + fprintf(mapFile, "# Address\tSize \tFile Name\n"); + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + //bool isCstring = (sect->type() == ld::Section::typeCString); + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + char buffer[4096]; + const ld::Atom* atom = *ait; + const char* name = atom->name(); + if ( atom->contentType() == ld::Atom::typeCString ) { + strcpy(buffer, "literal string: "); + strlcat(buffer, (char*)atom->rawContentPointer(), 4096); + name = buffer; + } + else if ( (atom->contentType() == ld::Atom::typeCFI) && (strcmp(name, "FDE") == 0) ) { + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( (fit->kind == ld::Fixup::kindSetTargetAddress) && (fit->clusterSize == ld::Fixup::k1of4) ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target->section().type() == ld::Section::typeCode) { + strcpy(buffer, "FDE for: "); + strlcat(buffer, fit->u.target->name(), 4096); + name = buffer; + } + } + } + } + else if ( atom->contentType() == ld::Atom::typeNonLazyPointer ) { + strcpy(buffer, "non-lazy-pointer"); + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingsIndirectlyBound ) { + strcpy(buffer, "non-lazy-pointer-to: "); + strlcat(buffer, state.indirectBindingTable[fit->u.bindingIndex]->name(), 4096); + break; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + strcpy(buffer, "non-lazy-pointer-to-local: "); + strlcat(buffer, fit->u.target->name(), 4096); + break; + } + } + name = buffer; + } + fprintf(mapFile, "0x%08llX\t0x%08llX\t[%3u] %s\n", atom->finalAddress(), atom->size(), + readerToFileOrdinal[atom->file()], name); + } + } + fclose(mapFile); + } + else { + warning("could not write map file: %s\n", _options.generatedMapPath()); + } + } +} + + +// used to sort atoms with debug notes +class DebugNoteSorter +{ +public: + bool operator()(const ld::Atom* left, const ld::Atom* right) const + { + // first sort by reader + uint32_t leftFileOrdinal = left->file()->ordinal(); + uint32_t rightFileOrdinal = right->file()->ordinal(); + if ( leftFileOrdinal!= rightFileOrdinal) + return (leftFileOrdinal < rightFileOrdinal); + + // then sort by atom objectAddress + uint64_t leftAddr = left->objectAddress(); + uint64_t rightAddr = right->objectAddress(); + return leftAddr < rightAddr; + } +}; + + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +const char* OutputFile::assureFullPath(const char* path) +{ + if ( path[0] == '/' ) + return path; + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + char* result; + asprintf(&result, "%s/%s", cwdbuff, path); + if ( result != NULL ) + return result; + } + return path; +} + +void OutputFile::synthesizeDebugNotes(ld::Internal& state) +{ + // -S means don't synthesize debug map + if ( _options.debugInfoStripping() == Options::kDebugInfoNone ) + return; + // make a vector of atoms that come from files compiled with dwarf debug info + std::vector<const ld::Atom*> atomsNeedingDebugNotes; + std::set<const ld::Atom*> atomsWithStabs; + atomsNeedingDebugNotes.reserve(1024); + const ld::relocatable::File* objFile = NULL; + bool objFileHasDwarf = false; + bool objFileHasStabs = false; + for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + // no stabs for atoms that would not be in the symbol table + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotIn ) + continue; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages ) + continue; + // no stabs for absolute symbols + if ( atom->definition() == ld::Atom::definitionAbsolute ) + continue; + // no stabs for .eh atoms + if ( atom->contentType() == ld::Atom::typeCFI ) + continue; + const ld::File* file = atom->file(); + if ( file != NULL ) { + if ( file != objFile ) { + objFileHasDwarf = false; + objFileHasStabs = false; + objFile = dynamic_cast<const ld::relocatable::File*>(file); + if ( objFile != NULL ) { + switch ( objFile->debugInfo() ) { + case ld::relocatable::File::kDebugInfoNone: + break; + case ld::relocatable::File::kDebugInfoDwarf: + objFileHasDwarf = true; + break; + case ld::relocatable::File::kDebugInfoStabs: + case ld::relocatable::File::kDebugInfoStabsUUID: + objFileHasStabs = true; + break; + } + } + } + if ( objFileHasDwarf ) + atomsNeedingDebugNotes.push_back(atom); + if ( objFileHasStabs ) + atomsWithStabs.insert(atom); + } + } + } + + // sort by file ordinal then atom ordinal + std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(), DebugNoteSorter()); + + // synthesize "debug notes" and add them to master stabs vector + const char* dirPath = NULL; + const char* filename = NULL; + bool wroteStartSO = false; + state.stabs.reserve(atomsNeedingDebugNotes.size()*4); + __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> seenFiles; + for (std::vector<const ld::Atom*>::iterator it=atomsNeedingDebugNotes.begin(); it != atomsNeedingDebugNotes.end(); it++) { + const ld::Atom* atom = *it; + const ld::File* atomFile = atom->file(); + const ld::relocatable::File* atomObjFile = dynamic_cast<const ld::relocatable::File*>(atomFile); + const char* newDirPath; + const char* newFilename; + //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); + if ( atom->translationUnitSource(&newDirPath, &newFilename) ) { + // need SO's whenever the translation unit source file changes + if ( newFilename != filename ) { + // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' + if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) + asprintf((char**)&newDirPath, "%s/", newDirPath); + if ( filename != NULL ) { + // translation unit change, emit ending SO + ld::relocatable::File::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + state.stabs.push_back(endFileStab); + } + // new translation unit, emit start SO's + ld::relocatable::File::Stab dirPathStab; + dirPathStab.atom = NULL; + dirPathStab.type = N_SO; + dirPathStab.other = 0; + dirPathStab.desc = 0; + dirPathStab.value = 0; + dirPathStab.string = newDirPath; + state.stabs.push_back(dirPathStab); + ld::relocatable::File::Stab fileStab; + fileStab.atom = NULL; + fileStab.type = N_SO; + fileStab.other = 0; + fileStab.desc = 0; + fileStab.value = 0; + fileStab.string = newFilename; + state.stabs.push_back(fileStab); + // Synthesize OSO for start of file + ld::relocatable::File::Stab objStab; + objStab.atom = NULL; + objStab.type = N_OSO; + // <rdar://problem/6337329> linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries + objStab.other = atomFile->cpuSubType(); + objStab.desc = 1; + if ( atomObjFile != NULL ) { + objStab.string = assureFullPath(atomObjFile->debugInfoPath()); + objStab.value = atomObjFile->debugInfoModificationTime(); + } + else { + objStab.string = assureFullPath(atomFile->path()); + objStab.value = atomFile->modificationTime(); + } + state.stabs.push_back(objStab); + wroteStartSO = true; + // add the source file path to seenFiles so it does not show up in SOLs + seenFiles.insert(newFilename); + char* fullFilePath; + asprintf(&fullFilePath, "%s/%s", newDirPath, newFilename); + // add both leaf path and full path + seenFiles.insert(fullFilePath); + } + filename = newFilename; + dirPath = newDirPath; + if ( atom->section().type() == ld::Section::typeCode ) { + // Synthesize BNSYM and start FUN stabs + ld::relocatable::File::Stab beginSym; + beginSym.atom = atom; + beginSym.type = N_BNSYM; + beginSym.other = 1; + beginSym.desc = 0; + beginSym.value = 0; + beginSym.string = ""; + state.stabs.push_back(beginSym); + ld::relocatable::File::Stab startFun; + startFun.atom = atom; + startFun.type = N_FUN; + startFun.other = 1; + startFun.desc = 0; + startFun.value = 0; + startFun.string = atom->name(); + state.stabs.push_back(startFun); + // Synthesize any SOL stabs needed + const char* curFile = NULL; + for (ld::Atom::LineInfo::iterator lit = atom->beginLineInfo(); lit != atom->endLineInfo(); ++lit) { + if ( lit->fileName != curFile ) { + if ( seenFiles.count(lit->fileName) == 0 ) { + seenFiles.insert(lit->fileName); + ld::relocatable::File::Stab sol; + sol.atom = 0; + sol.type = N_SOL; + sol.other = 0; + sol.desc = 0; + sol.value = 0; + sol.string = lit->fileName; + state.stabs.push_back(sol); + } + curFile = lit->fileName; + } + } + // Synthesize end FUN and ENSYM stabs + ld::relocatable::File::Stab endFun; + endFun.atom = atom; + endFun.type = N_FUN; + endFun.other = 0; + endFun.desc = 0; + endFun.value = 0; + endFun.string = ""; + state.stabs.push_back(endFun); + ld::relocatable::File::Stab endSym; + endSym.atom = atom; + endSym.type = N_ENSYM; + endSym.other = 1; + endSym.desc = 0; + endSym.value = 0; + endSym.string = ""; + state.stabs.push_back(endSym); + } + else { + ld::relocatable::File::Stab globalsStab; + const char* name = atom->name(); + if ( atom->scope() == ld::Atom::scopeTranslationUnit ) { + // Synthesize STSYM stab for statics + globalsStab.atom = atom; + globalsStab.type = N_STSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + state.stabs.push_back(globalsStab); + } + else { + // Synthesize GSYM stab for other globals + globalsStab.atom = atom; + globalsStab.type = N_GSYM; + globalsStab.other = 1; + globalsStab.desc = 0; + globalsStab.value = 0; + globalsStab.string = name; + state.stabs.push_back(globalsStab); + } + } + } + } + + if ( wroteStartSO ) { + // emit ending SO + ld::relocatable::File::Stab endFileStab; + endFileStab.atom = NULL; + endFileStab.type = N_SO; + endFileStab.other = 1; + endFileStab.desc = 0; + endFileStab.value = 0; + endFileStab.string = ""; + state.stabs.push_back(endFileStab); + } + + // copy any stabs from .o file + std::set<const ld::File*> filesSeenWithStabs; + for (std::set<const ld::Atom*>::iterator it=atomsWithStabs.begin(); it != atomsWithStabs.end(); it++) { + const ld::Atom* atom = *it; + objFile = dynamic_cast<const ld::relocatable::File*>(atom->file()); + if ( objFile != NULL ) { + if ( filesSeenWithStabs.count(objFile) == 0 ) { + filesSeenWithStabs.insert(objFile); + const std::vector<ld::relocatable::File::Stab>* stabs = objFile->stabs(); + if ( stabs != NULL ) { + for(std::vector<ld::relocatable::File::Stab>::const_iterator sit = stabs->begin(); sit != stabs->end(); ++sit) { + ld::relocatable::File::Stab stab = *sit; + // ignore stabs associated with atoms that were dead stripped or coalesced away + if ( (sit->atom != NULL) && (atomsWithStabs.count(sit->atom) == 0) ) + continue; + // <rdar://problem/8284718> Value of N_SO stabs should be address of first atom from translation unit + if ( (stab.type == N_SO) && (stab.string != NULL) && (stab.string[0] != '\0') ) { + stab.atom = atom; + } + state.stabs.push_back(stab); + } + } + } + } + } + +} + + +} // namespace tool +} // namespace ld + diff --git a/src/ld/OutputFile.h b/src/ld/OutputFile.h new file mode 100644 index 0000000..9b49289 --- /dev/null +++ b/src/ld/OutputFile.h @@ -0,0 +1,289 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __OUTPUT_FILE_H__ +#define __OUTPUT_FILE_H__ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <mach/mach_time.h> +#include <mach/vm_statistics.h> +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> + +#include <vector> + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + +class OutputFile +{ +public: + OutputFile(const Options& opts); + + + // iterates all atoms in initial files + void write(ld::Internal&); + bool findSegment(ld::Internal& state, uint64_t addr, uint64_t* start, uint64_t* end, uint32_t* index); + void setLazyBindingInfoOffset(uint64_t lpAddress, uint32_t lpInfoOffset); + uint32_t dylibCount(); + const ld::dylib::File* dylibByOrdinal(unsigned int ordinal); + uint32_t dylibToOrdinal(const ld::dylib::File*); + uint32_t encryptedTextStartOffset() { return _encryptedTEXTstartOffset; } + uint32_t encryptedTextEndOffset() { return _encryptedTEXTendOffset; } + int compressedOrdinalForAtom(const ld::Atom* target); + + + + bool hasWeakExternalSymbols; + bool usesWeakExternalSymbols; + bool overridesWeakExternalSymbols; + bool _noReExportedDylibs; + bool hasThreadLocalVariableDefinitions; + bool pieDisabled; + ld::Internal::FinalSection* headerAndLoadCommandsSection; + ld::Internal::FinalSection* rebaseSection; + ld::Internal::FinalSection* bindingSection; + ld::Internal::FinalSection* weakBindingSection; + ld::Internal::FinalSection* lazyBindingSection; + ld::Internal::FinalSection* exportSection; + ld::Internal::FinalSection* splitSegInfoSection; + ld::Internal::FinalSection* functionStartsSection; + ld::Internal::FinalSection* symbolTableSection; + ld::Internal::FinalSection* stringPoolSection; + ld::Internal::FinalSection* localRelocationsSection; + ld::Internal::FinalSection* externalRelocationsSection; + ld::Internal::FinalSection* sectionRelocationsSection; + ld::Internal::FinalSection* indirectSymbolTableSection; + + struct RebaseInfo { + RebaseInfo(uint8_t t, uint64_t addr) : _type(t), _address(addr) {} + uint8_t _type; + uint64_t _address; + // for sorting + int operator<(const RebaseInfo& rhs) const { + // sort by type, then address + if ( this->_type != rhs._type ) + return (this->_type < rhs._type ); + return (this->_address < rhs._address ); + } + }; + + struct BindingInfo { + BindingInfo(uint8_t t, int ord, const char* sym, bool weak_import, uint64_t addr, int64_t add) + : _type(t), _flags(weak_import ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0 ), _libraryOrdinal(ord), + _symbolName(sym), _address(addr), _addend(add) {} + BindingInfo(uint8_t t, const char* sym, bool non_weak_definition, uint64_t addr, int64_t add) + : _type(t), _flags(non_weak_definition ? BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION : 0 ), + _libraryOrdinal(0), _symbolName(sym), _address(addr), _addend(add) {} + uint8_t _type; + uint8_t _flags; + int _libraryOrdinal; + const char* _symbolName; + uint64_t _address; + int64_t _addend; + + // for sorting + int operator<(const BindingInfo& rhs) const { + // sort by library, symbol, type, then address + if ( this->_libraryOrdinal != rhs._libraryOrdinal ) + return (this->_libraryOrdinal < rhs._libraryOrdinal ); + if ( this->_symbolName != rhs._symbolName ) + return ( strcmp(this->_symbolName, rhs._symbolName) < 0 ); + if ( this->_type != rhs._type ) + return (this->_type < rhs._type ); + return (this->_address < rhs._address ); + } + }; + + struct SplitSegInfoEntry { + SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k) : address(a), kind(k) {} + uint64_t address; + ld::Fixup::Kind kind; + }; + +private: + void buildDylibOrdinalMapping(ld::Internal&); + bool hasOrdinalForInstallPath(const char* path, int* ordinal); + void addLoadCommands(ld::Internal& state); + void addLinkEdit(ld::Internal& state); + void addPreloadLinkEdit(ld::Internal& state); + void generateLinkEditInfo(ld::Internal& state); + void buildSymbolTable(ld::Internal& state); + void writeOutputFile(ld::Internal& state); + void assignFileOffsets(ld::Internal& state); + void setSectionSizesAndAlignments(ld::Internal& state); + void addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, + const ld::Atom* atom, ld::Fixup* fixupWithTarget, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend); + void addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* sect, + const ld::Atom* atom, ld::Fixup* fixupWithTarget, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend); + void addClassicRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, + const ld::Atom* atom, ld::Fixup* fixupWithTarget, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + const ld::Atom* target, const ld::Atom* minusTarget, + uint64_t targetAddend, uint64_t minusTargetAddend); + bool useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, + ld::Fixup* fixupWithTarget); + uint64_t pageAlign(uint64_t addr); + uint64_t pageAlign(uint64_t addr, uint64_t pageSize); + void setLoadCommandsPadding(ld::Internal& state); + void assignAtomAddresses(ld::Internal& state); + void addRebaseInfo(const ld::Atom* atom, const ld::Fixup* fixup, const ld::Atom* target); + void makeRebasingInfo(ld::Internal& state); + void makeBindingInfo(ld::Internal& state); + void updateLINKEDITAddresses(ld::Internal& state); + void applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer); + uint64_t addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target); + bool targetIsThumb(ld::Internal& state, const ld::Fixup* fixup); + uint32_t lazyBindingInfoOffsetForLazyPointerAddress(uint64_t lpAddress); + void copyNoOps(uint8_t* from, uint8_t* to); + bool isPointerToTarget(ld::Fixup::Kind kind); + bool isPointerFromTarget(ld::Fixup::Kind kind); + bool isPcRelStore(ld::Fixup::Kind kind); + bool isStore(ld::Fixup::Kind kind); + bool storeAddendOnly(const ld::Atom* inAtom, const ld::Atom* target, bool pcRel=false); + bool setsTarget(ld::Fixup::Kind kind); + void addFixupOutInfo(ld::Internal& state); + void makeRelocations(ld::Internal& state); + void makeSectionRelocations(ld::Internal& state); + void makeDyldInfo(ld::Internal& state); + void makeSplitSegInfo(ld::Internal& state); + void writeMapFile(ld::Internal& state); + uint64_t lookBackAddend(ld::Fixup::iterator fit); + bool takesNoDiskSpace(const ld::Section* sect); + bool hasZeroForFileOffset(const ld::Section* sect); + + void printSectionLayout(ld::Internal& state); + void rangeCheck8(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheck16(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckBranch32(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckRIP32(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckARM12(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckARMBranch24(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckThumbBranch22(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckPPCBranch24(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckPPCBranch14(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + uint64_t sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); + uint64_t tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); + void dumpAtomsBySection(ld::Internal& state, bool); + void synthesizeDebugNotes(ld::Internal& state); + const char* assureFullPath(const char* path); + void noteTextReloc(const ld::Atom* atom, const ld::Atom* target); + + + static uint16_t get16LE(uint8_t* loc); + static void set16LE(uint8_t* loc, uint16_t value); + static uint32_t get32LE(uint8_t* loc); + static void set32LE(uint8_t* loc, uint32_t value); + static uint64_t get64LE(uint8_t* loc); + static void set64LE(uint8_t* loc, uint64_t value); + + static uint16_t get16BE(uint8_t* loc); + static void set16BE(uint8_t* loc, uint16_t value); + static uint32_t get32BE(uint8_t* loc); + static void set32BE(uint8_t* loc, uint32_t value); + static uint64_t get64BE(uint8_t* loc); + static void set64BE(uint8_t* loc, uint64_t value); + + + + const Options& _options; + std::map<const ld::dylib::File*, int> _dylibToOrdinal; + std::vector<const ld::dylib::File*> _dylibsToLoad; + std::vector<const char*> _dylibOrdinalPaths; + const bool _hasDyldInfo; + const bool _hasSymbolTable; + const bool _hasSectionRelocations; + const bool _hasSplitSegInfo; + const bool _hasFunctionStartsInfo; + bool _hasDynamicSymbolTable; + bool _hasLocalRelocations; + bool _hasExternalRelocations; + uint64_t _fileSize; + std::map<uint64_t, uint32_t> _lazyPointerAddressToInfoOffset; + uint32_t _encryptedTEXTstartOffset; + uint32_t _encryptedTEXTendOffset; +public: + std::vector<const ld::Atom*> _localAtoms; + std::vector<const ld::Atom*> _exportedAtoms; + std::vector<const ld::Atom*> _importedAtoms; + uint32_t _localSymbolsStartIndex; + uint32_t _localSymbolsCount; + uint32_t _globalSymbolsStartIndex; + uint32_t _globalSymbolsCount; + uint32_t _importSymbolsStartIndex; + uint32_t _importSymbolsCount; + std::map<const ld::Atom*, uint32_t> _atomToSymbolIndex; + std::vector<RebaseInfo> _rebaseInfo; + std::vector<BindingInfo> _bindingInfo; + std::vector<BindingInfo> _lazyBindingInfo; + std::vector<BindingInfo> _weakBindingInfo; + std::vector<SplitSegInfoEntry> _splitSegInfos; + class HeaderAndLoadCommandsAbtract* _headersAndLoadCommandAtom; + class RelocationsAtomAbstract* _sectionsRelocationsAtom; + class RelocationsAtomAbstract* _localRelocsAtom; + class RelocationsAtomAbstract* _externalRelocsAtom; + class ClassicLinkEditAtom* _symbolTableAtom; + class ClassicLinkEditAtom* _indirectSymbolTableAtom; + class StringPoolAtom* _stringPoolAtom; + class LinkEditAtom* _rebasingInfoAtom; + class LinkEditAtom* _bindingInfoAtom; + class LinkEditAtom* _lazyBindingInfoAtom; + class LinkEditAtom* _weakBindingInfoAtom; + class LinkEditAtom* _exportInfoAtom; + class LinkEditAtom* _splitSegInfoAtom; + class LinkEditAtom* _functionStartsAtom; +}; + +} // namespace tool +} // namespace ld + +#endif // __OUTPUT_FILE_H__ diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp new file mode 100644 index 0000000..e17e632 --- /dev/null +++ b/src/ld/Resolver.cpp @@ -0,0 +1,1402 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <mach/mach_time.h> +#include <mach/vm_statistics.h> +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> +#include <mach-o/fat.h> + +#include <string> +#include <map> +#include <set> +#include <string> +#include <vector> +#include <list> +#include <algorithm> +#include <ext/hash_map> +#include <ext/hash_set> +#include <dlfcn.h> +#include <AvailabilityMacros.h> + +#include "Options.h" + +#include "ld.hpp" +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Resolver.h" +#include "parsers/lto_file.h" + + +namespace ld { +namespace tool { + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +class UndefinedProxyAtom : public ld::Atom +{ +public: + UndefinedProxyAtom(const char* nm) + : ld::Atom(_s_section, ld::Atom::definitionProxy, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, + ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(0)), + _name(nm) {} + // overrides of ld::Atom + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +protected: + + virtual ~UndefinedProxyAtom() {} + + const char* _name; + + static ld::Section _s_section; +}; + +ld::Section UndefinedProxyAtom::_s_section("__TEXT", "__import", ld::Section::typeImportProxies, true); + + + + +class AliasAtom : public ld::Atom +{ +public: + AliasAtom(const ld::Atom& target, const char* nm) : + ld::Atom(target.section(), target.definition(), ld::Atom::combineNever, + ld::Atom::scopeGlobal, target.contentType(), + target.symbolTableInclusion(), target.dontDeadStrip(), + target.isThumb(), true, target.alignment()), + _name(nm), + _aliasOf(target), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, &target) { } + + // overrides of ld::Atom + virtual const ld::File* file() const { return _aliasOf.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return _aliasOf.translationUnitSource(dir, nm); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return _aliasOf.objectAddress(); } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const + { return _aliasOf.contentHash(ibt); } + virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const + { return _aliasOf.canCoalesceWith(rhs,ibt); } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + virtual ld::Atom::UnwindInfo::iterator beginUnwind() const { return NULL; } + virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return NULL; } + virtual ld::Atom::LineInfo::iterator beginLineInfo() const { return NULL; } + virtual ld::Atom::LineInfo::iterator endLineInfo() const { return NULL; } + +private: + const char* _name; + const ld::Atom& _aliasOf; + ld::Fixup _fixup; +}; + + + +class SectionBoundaryAtom : public ld::Atom +{ +public: + static SectionBoundaryAtom* makeSectionBoundaryAtom(const char* name, bool start, const char* segSectName); + static SectionBoundaryAtom* makeOldSectionBoundaryAtom(const char* name, bool start); + + // overrides of ld::Atom + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual uint64_t objectAddress() const { return 0; } + +private: + + SectionBoundaryAtom(const char* nm, const ld::Section& sect, + ld::Atom::ContentType cont) : + ld::Atom(sect, + ld::Atom::definitionRegular, + ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, + cont, + ld::Atom::symbolTableNotIn, + false, false, true, ld::Atom::Alignment(0)), + _name(nm) { } + + const char* _name; +}; + +SectionBoundaryAtom* SectionBoundaryAtom::makeSectionBoundaryAtom(const char* name, bool start, const char* segSectName) +{ + + const char* segSectDividor = strrchr(segSectName, '$'); + if ( segSectDividor == NULL ) + throwf("malformed section$ symbol name: %s", name); + const char* sectionName = segSectDividor + 1; + int segNameLen = segSectDividor - segSectName; + if ( segNameLen > 16 ) + throwf("malformed section$ symbol name: %s", name); + char segName[18]; + strlcpy(segName, segSectName, segNameLen+1); + + const ld::Section* section = new ld::Section(strdup(segName), sectionName, ld::Section::typeUnclassified); + return new SectionBoundaryAtom(name, *section, (start ? ld::Atom::typeSectionStart : typeSectionEnd)); +} + +SectionBoundaryAtom* SectionBoundaryAtom::makeOldSectionBoundaryAtom(const char* name, bool start) +{ + // e.g. __DATA__bss__begin + char segName[18]; + strlcpy(segName, name, 7); + + char sectName[18]; + int nameLen = strlen(name); + strlcpy(sectName, &name[6], (start ? nameLen-12 : nameLen-10)); + warning("grandfathering in old symbol '%s' as alias for 'section$%s$%s$%s'", name, start ? "start" : "end", segName, sectName); + const ld::Section* section = new ld::Section(strdup(segName), strdup(sectName), ld::Section::typeUnclassified); + return new SectionBoundaryAtom(name, *section, (start ? ld::Atom::typeSectionStart : typeSectionEnd)); +} + + + + +class SegmentBoundaryAtom : public ld::Atom +{ +public: + static SegmentBoundaryAtom* makeSegmentBoundaryAtom(const char* name, bool start, const char* segName); + static SegmentBoundaryAtom* makeOldSegmentBoundaryAtom(const char* name, bool start); + + // overrides of ld::Atom + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual uint64_t objectAddress() const { return 0; } + +private: + + SegmentBoundaryAtom(const char* nm, const ld::Section& sect, + ld::Atom::ContentType cont) : + ld::Atom(sect, + ld::Atom::definitionRegular, + ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, + cont, + ld::Atom::symbolTableNotIn, + false, false, true, ld::Atom::Alignment(0)), + _name(nm) { } + + const char* _name; +}; + +SegmentBoundaryAtom* SegmentBoundaryAtom::makeSegmentBoundaryAtom(const char* name, bool start, const char* segName) +{ + if ( *segName == '\0' ) + throwf("malformed segment$ symbol name: %s", name); + if ( strlen(segName) > 16 ) + throwf("malformed segment$ symbol name: %s", name); + + if ( start ) { + const ld::Section* section = new ld::Section(segName, "__start", ld::Section::typeFirstSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionStart); + } + else { + const ld::Section* section = new ld::Section(segName, "__end", ld::Section::typeLastSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionEnd); + } +} + +SegmentBoundaryAtom* SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(const char* name, bool start) +{ + // e.g. __DATA__begin + char temp[18]; + strlcpy(temp, name, 7); + char* segName = strdup(temp); + + warning("grandfathering in old symbol '%s' as alias for 'segment$%s$%s'", name, start ? "start" : "end", segName); + + if ( start ) { + const ld::Section* section = new ld::Section(segName, "__start", ld::Section::typeFirstSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionStart); + } + else { + const ld::Section* section = new ld::Section(segName, "__end", ld::Section::typeLastSection, true); + return new SegmentBoundaryAtom(name, *section, ld::Atom::typeSectionEnd); + } +} + +void Resolver::initializeState() +{ + // set initial objc constraint based on command line options + if ( _options.objcGc() ) + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( _options.objcGcOnly() ) + _internal.objcObjectConstraint = ld::File::objcConstraintGC; + + _internal.cpuSubType = _options.subArchitecture(); +} + +void Resolver::buildAtomList() +{ + // each input files contributes initial atoms + _atoms.reserve(1024); + _inputFiles.forEachInitialAtom(*this); + _completedInitialObjectFiles = true; + + //_symbolTable.printStatistics(); +} + +unsigned int Resolver::ppcSubTypeIndex(uint32_t subtype) +{ + switch ( subtype ) { + case CPU_SUBTYPE_POWERPC_ALL: + return 0; + case CPU_SUBTYPE_POWERPC_750: + // G3 + return 1; + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + // G4 + return 2; + case CPU_SUBTYPE_POWERPC_970: + // G5 can run everything + return 3; + default: + throw "Unhandled PPC cpu subtype!"; + break; + } +} + +void Resolver::doFile(const ld::File& file) +{ + const ld::relocatable::File* objFile = dynamic_cast<const ld::relocatable::File*>(&file); + const ld::dylib::File* dylibFile = dynamic_cast<const ld::dylib::File*>(&file); + + if ( objFile != NULL ) { + // update which form of ObjC is being used + switch ( file.objCConstraint() ) { + case ld::File::objcConstraintNone: + break; + case ld::File::objcConstraintRetainRelease: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintGC ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); + if ( _options.objcGcOnly() ) + throwf("command line specified -objc_gc_only, but file is retain/release based: %s", file.path()); + if ( _options.objcGc() ) + throwf("command line specified -objc_gc, but file is retain/release based: %s", file.path()); + _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; + break; + case ld::File::objcConstraintRetainReleaseOrGC: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; + break; + case ld::File::objcConstraintGC: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintRetainRelease ) + throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); + _internal.objcObjectConstraint = ld::File::objcConstraintGC; + break; + } + + // in -r mode, if any .o files have dwarf then add UUID to output .o file + if ( objFile->debugInfo() == ld::relocatable::File::kDebugInfoDwarf ) + _internal.someObjectFileHasDwarf = true; + + // remember if any objc classes built for fix-and-continue + if ( objFile->objcReplacementClasses() ) + _internal.hasObjcReplacementClasses = true; + + // remember if any .o file did not have MH_SUBSECTIONS_VIA_SYMBOLS bit set + if ( ! objFile->canScatterAtoms() ) + _internal.allObjectFilesScatterable = false; + + // update cpu-sub-type + cpu_subtype_t nextObjectSubType = file.cpuSubType(); + switch ( _options.architecture() ) { + case CPU_TYPE_POWERPC: + // no checking when -force_cpusubtype_ALL is used + if ( _options.forceCpuSubtypeAll() ) + return; + if ( _options.preferSubArchitecture() ) { + // warn if some .o file is not compatible with desired output sub-type + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_options.subArchitecture()) ) { + if ( !_inputFiles.inferredArch() ) + warning("cpu-sub-type of %s is not compatible with command line cpu-sub-type", file.path()); + _internal.cpuSubType = nextObjectSubType; + } + } + } + else { + // command line to linker just had -arch ppc + // figure out final sub-type based on sub-type of all .o files + if ( ppcSubTypeIndex(nextObjectSubType) > ppcSubTypeIndex(_internal.cpuSubType) ) { + _internal.cpuSubType = nextObjectSubType; + } + } + break; + + case CPU_TYPE_ARM: + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( (_options.subArchitecture() == CPU_SUBTYPE_ARM_ALL) && _options.forceCpuSubtypeAll() ) { + // hack to support gcc multillib build that tries to make sub-type-all slice + } + else if ( nextObjectSubType == CPU_SUBTYPE_ARM_ALL ) { + warning("CPU_SUBTYPE_ARM_ALL subtype is deprecated: %s", file.path()); + } + else { + throwf("object file %s was built for different arm sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + } + break; + + case CPU_TYPE_POWERPC64: + break; + + case CPU_TYPE_I386: + _internal.cpuSubType = CPU_SUBTYPE_I386_ALL; + break; + + case CPU_TYPE_X86_64: + _internal.cpuSubType = CPU_SUBTYPE_X86_64_ALL; + break; + } + } + if ( dylibFile != NULL ) { + // update which form of ObjC dylibs are being linked + switch ( dylibFile->objCConstraint() ) { + case ld::File::objcConstraintNone: + break; + case ld::File::objcConstraintRetainRelease: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintGC ) + throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); + if ( _options.objcGcOnly() ) + throwf("command line specified -objc_gc_only, but dylib is retain/release based: %s", file.path()); + if ( _options.objcGc() ) + throwf("command line specified -objc_gc, but dylib is retain/release based: %s", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintRetainRelease; + break; + case ld::File::objcConstraintRetainReleaseOrGC: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) + _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseOrGC; + break; + case ld::File::objcConstraintGC: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintRetainRelease ) + throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintGC; + break; + } + } + +} + +void Resolver::doAtom(const ld::Atom& atom) +{ + //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s\n", &atom, atom.name(), atom.section().sectionName()); + + // add to list of known atoms + _atoms.push_back(&atom); + + // adjust scope + if ( _options.hasExportRestrictList() || _options.hasReExportList() ) { + const char* name = atom.name(); + switch ( atom.scope() ) { + case ld::Atom::scopeTranslationUnit: + break; + case ld::Atom::scopeLinkageUnit: + if ( _options.hasExportMaskList() && _options.shouldExport(name) ) { + // <rdar://problem/5062685> ld does not report error when -r is used and exported symbols are not defined. + if ( _options.outputKind() == Options::kObjectFile ) + throwf("cannot export hidden symbol %s", name); + // .objc_class_name_* symbols are special + if ( atom.section().type() != ld::Section::typeObjC1Classes ) { + if ( atom.definition() == ld::Atom::definitionProxy ) { + // .exp file says to export a symbol, but that symbol is in some dylib being linked + if ( _options.canReExportSymbols() ) { + // marking proxy atom as global triggers the re-export + (const_cast<ld::Atom*>(&atom))->setScope(ld::Atom::scopeGlobal); + } + else { + if ( atom.file() != NULL ) + warning("cannot re-export symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path()); + else + warning("cannot re-export symbol %s\n", SymbolTable::demangle(name)); + } + } + else { + if ( atom.file() != NULL ) + warning("cannot export hidden symbol %s from %s", SymbolTable::demangle(name), atom.file()->path()); + else + warning("cannot export hidden symbol %s", SymbolTable::demangle(name)); + } + } + } + else if ( _options.shouldReExport(name) && _options.canReExportSymbols() ) { + if ( atom.definition() == ld::Atom::definitionProxy ) { + // marking proxy atom as global triggers the re-export + (const_cast<ld::Atom*>(&atom))->setScope(ld::Atom::scopeGlobal); + } + else { + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path()); + } + } + break; + case ld::Atom::scopeGlobal: + // check for globals that are downgraded to hidden + if ( ! _options.shouldExport(name) ) { + (const_cast<ld::Atom*>(&atom))->setScope(ld::Atom::scopeLinkageUnit); + //fprintf(stderr, "demote %s to hidden\n", name); + } + if ( _options.canReExportSymbols() && _options.shouldReExport(name) ) { + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", SymbolTable::demangle(name), atom.file()->path()); + } + break; + } + } + + // work around for kernel that uses 'l' labels in assembly code + if ( (atom.symbolTableInclusion() == ld::Atom::symbolTableNotInFinalLinkedImages) + && (atom.name()[0] == 'l') && (_options.outputKind() == Options::kStaticExecutable) ) + (const_cast<ld::Atom*>(&atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); + + + // tell symbol table about non-static atoms + if ( atom.scope() != ld::Atom::scopeTranslationUnit ) { + _symbolTable.add(atom, _options.deadCodeStrip() && _completedInitialObjectFiles); + + // add symbol aliases defined on the command line + if ( _options.haveCmdLineAliases() ) { + const std::vector<Options::AliasPair>& aliases = _options.cmdLineAliases(); + for (std::vector<Options::AliasPair>::const_iterator it=aliases.begin(); it != aliases.end(); ++it) { + if ( strcmp(it->realName, atom.name()) == 0 ) { + const ld::Atom* alias = new AliasAtom(atom, it->alias); + this->doAtom(*alias); + } + } + } + } + + // convert references by-name or by-content to by-slot + this->convertReferencesToIndirect(atom); + + // remember if any atoms are proxies that require LTO + if ( atom.contentType() == ld::Atom::typeLTOtemporary ) + _haveLLVMObjs = true; + + // if we've already partitioned into final sections, and lto needs a symbol very late, add it + if ( _addToFinalSection ) + _internal.addAtom(atom); + + if ( _options.deadCodeStrip() ) { + // add to set of dead-strip-roots, all symbols that the compiler marks as don't strip + if ( atom.dontDeadStrip() ) + _deadStripRoots.insert(&atom); + + if ( atom.scope() == ld::Atom::scopeGlobal ) { + // <rdar://problem/5524973> -exported_symbols_list that has wildcards and -dead_strip + // in dylibs, every global atom in initial .o files is a root + if ( _options.hasWildCardExportRestrictList() || _options.allGlobalsAreDeadStripRoots() ) { + if ( _options.shouldExport(atom.name()) ) + _deadStripRoots.insert(&atom); + } + } + } +} + +bool Resolver::isDtraceProbe(ld::Fixup::Kind kind) +{ + switch (kind) { + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + case ld::Fixup::kindDtraceExtra: + return true; + default: + break; + } + return false; +} + +void Resolver::convertReferencesToIndirect(const ld::Atom& atom) +{ + // convert references by-name or by-content to by-slot + SymbolTable::IndirectBindingSlot slot; + const ld::Atom* dummy; + ld::Fixup::iterator end = atom.fixupsEnd(); + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != end; ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + if ( isDtraceProbe(fit->kind) && (_options.outputKind() != Options::kObjectFile ) ) { + // in final linked images, remove reference + fit->binding = ld::Fixup::bindingNone; + } + else { + slot = _symbolTable.findSlotForName(fit->u.name); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + } + break; + case ld::Fixup::bindingByContentBound: + switch ( fit->u.target->combine() ) { + case ld::Atom::combineNever: + case ld::Atom::combineByName: + assert(0 && "wrong combine type for bind by content"); + break; + case ld::Atom::combineByNameAndContent: + slot = _symbolTable.findSlotForContent(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + case ld::Atom::combineByNameAndReferences: + slot = _symbolTable.findSlotForReferences(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + } + break; + case ld::Fixup::bindingNone: + case ld::Fixup::bindingDirectlyBound: + case ld::Fixup::bindingsIndirectlyBound: + break; + } + } +} + + +void Resolver::addInitialUndefines() +{ + // add initial undefines from -u option + for (Options::UndefinesIterator it=_options.initialUndefinesBegin(); it != _options.initialUndefinesEnd(); ++it) { + _symbolTable.findSlotForName(*it); + } +} + +void Resolver::resolveUndefines() +{ + // keep looping until no more undefines were added in last loop + unsigned int undefineGenCount = 0xFFFFFFFF; + while ( undefineGenCount != _symbolTable.updateCount() ) { + undefineGenCount = _symbolTable.updateCount(); + std::vector<const char*> undefineNames; + _symbolTable.undefines(undefineNames); + for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + const char* undef = *it; + // load for previous undefine may also have loaded this undefine, so check again + if ( ! _symbolTable.hasName(undef) ) { + _inputFiles.searchLibraries(undef, true, true, *this); + if ( !_symbolTable.hasName(undef) && (_options.outputKind() != Options::kObjectFile) ) { + if ( strncmp(undef, "section$", 8) == 0 ) { + if ( strncmp(undef, "section$start$", 14) == 0 ) { + this->doAtom(*SectionBoundaryAtom::makeSectionBoundaryAtom(undef, true, &undef[14])); + } + else if ( strncmp(undef, "section$end$", 12) == 0 ) { + this->doAtom(*SectionBoundaryAtom::makeSectionBoundaryAtom(undef, false, &undef[12])); + } + } + else if ( strncmp(undef, "segment$", 8) == 0 ) { + if ( strncmp(undef, "segment$start$", 14) == 0 ) { + this->doAtom(*SegmentBoundaryAtom::makeSegmentBoundaryAtom(undef, true, &undef[14])); + } + else if ( strncmp(undef, "segment$end$", 12) == 0 ) { + this->doAtom(*SegmentBoundaryAtom::makeSegmentBoundaryAtom(undef, false, &undef[12])); + } + } + else if ( _options.outputKind() == Options::kPreload ) { + // for iBoot grandfather in old style section labels + int undefLen = strlen(undef); + if ( strcmp(&undef[undefLen-7], "__begin") == 0 ) { + if ( undefLen > 13 ) + this->doAtom(*SectionBoundaryAtom::makeOldSectionBoundaryAtom(undef, true)); + else + this->doAtom(*SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(undef, true)); + } + else if ( strcmp(&undef[undefLen-5], "__end") == 0 ) { + if ( undefLen > 11 ) + this->doAtom(*SectionBoundaryAtom::makeOldSectionBoundaryAtom(undef, false)); + else + this->doAtom(*SegmentBoundaryAtom::makeOldSegmentBoundaryAtom(undef, false)); + } + } + } + } + } + // <rdar://problem/5894163> need to search archives for overrides of common symbols + if ( _symbolTable.hasExternalTentativeDefinitions() ) { + bool searchDylibs = (_options.commonsMode() == Options::kCommonsOverriddenByDylibs); + std::vector<const char*> tents; + _symbolTable.tentativeDefs(tents); + for(std::vector<const char*>::iterator it = tents.begin(); it != tents.end(); ++it) { + // load for previous tentative may also have loaded this tentative, so check again + const ld::Atom* curAtom = _symbolTable.atomForSlot(_symbolTable.findSlotForName(*it)); + assert(curAtom != NULL); + if ( curAtom->definition() == ld::Atom::definitionTentative ) { + _inputFiles.searchLibraries(*it, searchDylibs, true, *this); + } + } + } + } + + // create proxies as needed for undefined symbols + if ( (_options.undefinedTreatment() != Options::kUndefinedError) || (_options.outputKind() == Options::kObjectFile) ) { + std::vector<const char*> undefineNames; + _symbolTable.undefines(undefineNames); + for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + // make proxy + this->doAtom(*new UndefinedProxyAtom(*it)); + } + } + + // support -U option + if ( _options.someAllowedUndefines() ) { + std::vector<const char*> undefineNames; + _symbolTable.undefines(undefineNames); + for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + if ( _options.allowedUndefined(*it) ) { + // make proxy + this->doAtom(*new UndefinedProxyAtom(*it)); + } + } + } + +} + + +void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) +{ + //fprintf(stderr, "markLive(%p) %s\n", &atom, atom.name()); + // if -why_live cares about this symbol, then dump chain + if ( (previous->referer != NULL) && _options.printWhyLive(atom.name()) ) { + fprintf(stderr, "%s from %s\n", atom.name(), atom.file()->path()); + int depth = 1; + for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { + for(int i=depth; i > 0; --i) + fprintf(stderr, " "); + fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->file()->path()); + } + } + + // if already marked live, then done (stop recursion) + if ( atom.live() ) + return; + + // mark this atom is live + (const_cast<ld::Atom*>(&atom))->setLive(); + + // mark all atoms it references as live + WhyLiveBackChain thisChain; + thisChain.previous = previous; + thisChain.referer = &atom; + for (ld::Fixup::iterator fit = atom.fixupsBegin(), end=atom.fixupsEnd(); fit != end; ++fit) { + const ld::Atom* target; + switch ( fit->kind ) { + case ld::Fixup::kindNone: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindSetTargetAddress: + case ld::Fixup::kindSubtractTargetAddress: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + case ld::Fixup::kindStoreTargetAddressBigEndian32: + case ld::Fixup::kindStoreTargetAddressBigEndian64: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + // normally this was done in convertReferencesToIndirect() + // but a archive loaded .o file may have a forward reference + SymbolTable::IndirectBindingSlot slot; + const ld::Atom* dummy; + switch ( fit->u.target->combine() ) { + case ld::Atom::combineNever: + case ld::Atom::combineByName: + assert(0 && "wrong combine type for bind by content"); + break; + case ld::Atom::combineByNameAndContent: + slot = _symbolTable.findSlotForContent(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + case ld::Atom::combineByNameAndReferences: + slot = _symbolTable.findSlotForReferences(fit->u.target, &dummy); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + fit->u.bindingIndex = slot; + break; + } + } + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + markLive(*(fit->u.target), &thisChain); + break; + case ld::Fixup::bindingByNameUnbound: + // doAtom() did not convert to indirect in dead-strip mode, so that now + fit->u.bindingIndex = _symbolTable.findSlotForName(fit->u.name); + fit->binding = ld::Fixup::bindingsIndirectlyBound; + // fall into next case + case ld::Fixup::bindingsIndirectlyBound: + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + if ( target == NULL ) { + const char* targetName = _symbolTable.indirectName(fit->u.bindingIndex); + _inputFiles.searchLibraries(targetName, true, true, *this); + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + } + if ( target != NULL ) { + if ( target->definition() == ld::Atom::definitionTentative ) { + // <rdar://problem/5894163> need to search archives for overrides of common symbols + bool searchDylibs = (_options.commonsMode() == Options::kCommonsOverriddenByDylibs); + _inputFiles.searchLibraries(target->name(), searchDylibs, true, *this); + // recompute target since it may have been overridden by searchLibraries() + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + } + this->markLive(*target, &thisChain); + } + else { + _atomsWithUnresolvedReferences.push_back(&atom); + } + break; + default: + assert(0 && "bad binding during dead stripping"); + } + break; + default: + break; + } + } + +} + + +void Resolver::deadStripOptimize() +{ + // only do this optimization with -dead_strip + if ( ! _options.deadCodeStrip() ) + return; + + // add entry point (main) to live roots + const ld::Atom* entry = this->entryPoint(true); + if ( entry != NULL ) + _deadStripRoots.insert(entry); + + // add -exported_symbols_list, -init, and -u entries to live roots + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(*uit); + if ( _internal.indirectBindingTable[slot] == NULL ) { + _inputFiles.searchLibraries(*uit, false, true, *this); + } + if ( _internal.indirectBindingTable[slot] != NULL ) + _deadStripRoots.insert(_internal.indirectBindingTable[slot]); + } + + // this helper is only referenced by synthesize stubs, assume it will be used + if ( _internal.classicBindingHelper != NULL ) + _deadStripRoots.insert(_internal.classicBindingHelper); + + // this helper is only referenced by synthesize stubs, assume it will be used + if ( _internal.compressedFastBinderProxy != NULL ) + _deadStripRoots.insert(_internal.compressedFastBinderProxy); + + // this helper is only referenced by synthesized lazy stubs, assume it will be used + if ( _internal.lazyBindingHelper != NULL ) + _deadStripRoots.insert(_internal.lazyBindingHelper); + + // add all dont-dead-strip atoms as roots + for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->dontDeadStrip() ) { + //fprintf(stderr, "dont dead strip: %p %s %s\n", atom, atom->section().sectionName(), atom->name()); + _deadStripRoots.insert(atom); + // unset liveness, so markLive() will recurse + (const_cast<ld::Atom*>(atom))->setLive(0); + } + } + + // mark all roots as live, and all atoms they reference + for (std::set<const ld::Atom*>::iterator it=_deadStripRoots.begin(); it != _deadStripRoots.end(); ++it) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.referer = *it; + this->markLive(**it, &rootChain); + } + + // now remove all non-live atoms from _atoms + const bool log = false; + if ( log ) { + fprintf(stderr, "deadStripOptimize() all atoms with liveness:\n"); + for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + fprintf(stderr, " live=%d name=%s\n", (*it)->live(), (*it)->name()); + } + } + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); +} + + +void Resolver::liveUndefines(std::vector<const char*>& undefs) +{ + StringSet undefSet; + // search all live atoms for references that are unbound + for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + assert(atom->live()); + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( (ld::Fixup::TargetBinding)fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + assert(0 && "should not be by-name this late"); + undefSet.insert(fit->u.name); + break; + case ld::Fixup::bindingsIndirectlyBound: + if ( _internal.indirectBindingTable[fit->u.bindingIndex] == NULL ) { + undefSet.insert(_symbolTable.indirectName(fit->u.bindingIndex)); + } + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingNone: + case ld::Fixup::bindingDirectlyBound: + break; + } + } + } + // look for any initial undefines that are still undefined + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + if ( ! _symbolTable.hasName(*uit) ) { + undefSet.insert(*uit); + } + } + + // copy set to vector + for (StringSet::const_iterator it=undefSet.begin(); it != undefSet.end(); ++it) { + undefs.push_back(*it); + } +} + + + +// <rdar://problem/8252819> warn when .objc_class_name_* symbol missing +class ExportedObjcClass +{ +public: + ExportedObjcClass(const Options& opt) : _options(opt) {} + + bool operator()(const char* name) const { + if ( (strncmp(name, ".objc_class_name_", 17) == 0) && _options.shouldExport(name) ) { + warning("ignoring undefined symbol %s from -exported_symbols_list", name); + return true; + } + const char* s = strstr(name, "CLASS_$_"); + if ( s != NULL ) { + char temp[strlen(name)+16]; + strcpy(temp, ".objc_class_name_"); + strcat(temp, &s[8]); + if ( _options.wasRemovedExport(temp) ) { + warning("ignoring undefined symbol %s from -exported_symbols_list", temp); + return true; + } + } + return false; + } +private: + const Options& _options; +}; + + +// temp hack for undefined aliases +class UndefinedAlias +{ +public: + UndefinedAlias(const Options& opt) : _aliases(opt.cmdLineAliases()) {} + + bool operator()(const char* name) const { + for (std::vector<Options::AliasPair>::const_iterator it=_aliases.begin(); it != _aliases.end(); ++it) { + if ( strcmp(it->realName, name) == 0 ) { + warning("undefined base symbol '%s' for alias '%s'", name, it->alias); + return true; + } + } + return false; + } +private: + const std::vector<Options::AliasPair>& _aliases; +}; + + + +static const char* pathLeafName(const char* path) +{ + const char* shortPath = strrchr(path, '/'); + if ( shortPath == NULL ) + return path; + else + return &shortPath[1]; +} + +bool Resolver::printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot) +{ + unsigned foundReferenceCount = 0; + for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingsIndirectlyBound ) { + if ( fit->u.bindingIndex == slot ) { + if ( atom->contentType() == ld::Atom::typeNonLazyPointer ) { + const ld::Atom* existingAtom; + unsigned int nlSlot = _symbolTable.findSlotForReferences(atom, &existingAtom); + if ( printReferencedBy(name, nlSlot) ) + ++foundReferenceCount; + } + else if ( atom->contentType() == ld::Atom::typeCFI ) { + fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->file()->path())); + ++foundReferenceCount; + } + else { + fprintf(stderr, " %s in %s\n", SymbolTable::demangle(atom->name()), pathLeafName(atom->file()->path())); + ++foundReferenceCount; + break; // if undefined used twice in a function, only show first + } + } + } + } + if ( foundReferenceCount > 6 ) { + fprintf(stderr, " ...\n"); + break; // only show first six uses of undefined symbol + } + } + return (foundReferenceCount != 0); +} + +void Resolver::checkUndefines(bool force) +{ + // when using LTO, undefines are checked after bitcode is optimized + if ( _haveLLVMObjs && !force ) + return; + + // error out on any remaining undefines + bool doPrint = true; + bool doError = true; + switch ( _options.undefinedTreatment() ) { + case Options::kUndefinedError: + break; + case Options::kUndefinedDynamicLookup: + doError = false; + break; + case Options::kUndefinedWarning: + doError = false; + break; + case Options::kUndefinedSuppress: + doError = false; + doPrint = false; + break; + } + std::vector<const char*> unresolvableUndefines; + if ( _options.deadCodeStrip() ) + this->liveUndefines(unresolvableUndefines); + else + _symbolTable.undefines(unresolvableUndefines); + + // <rdar://problem/8252819> assert when .objc_class_name_* symbol missing + if ( _options.hasExportMaskList() ) { + unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(_options)), unresolvableUndefines.end()); + } + + // hack to temporarily make missing aliases a warning + if ( _options.haveCmdLineAliases() ) { + unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), UndefinedAlias(_options)), unresolvableUndefines.end()); + } + + const int unresolvableCount = unresolvableUndefines.size(); + int unresolvableExportsCount = 0; + if ( unresolvableCount != 0 ) { + if ( doPrint ) { + if ( _options.printArchPrefix() ) + fprintf(stderr, "Undefined symbols for architecture %s:\n", _options.architectureName()); + else + fprintf(stderr, "Undefined symbols:\n"); + for (int i=0; i < unresolvableCount; ++i) { + const char* name = unresolvableUndefines[i]; + unsigned int slot = _symbolTable.findSlotForName(name); + fprintf(stderr, " \"%s\", referenced from:\n", SymbolTable::demangle(name)); + // scan all atoms for references + bool foundAtomReference = printReferencedBy(name, slot); + // scan command line options + if ( !foundAtomReference ) { + // might be from -init command line option + if ( (_options.initFunctionName() != NULL) && (strcmp(name, _options.initFunctionName()) == 0) ) { + fprintf(stderr, " -init command line option\n"); + } + // or might be from exported symbol option + else if ( _options.hasExportMaskList() && _options.shouldExport(name) ) { + fprintf(stderr, " -exported_symbol[s_list] command line option\n"); + } + // or might be from re-exported symbol option + else if ( _options.hasReExportList() && _options.shouldReExport(name) ) { + fprintf(stderr, " -reexported_symbols_list command line option\n"); + } + else { + bool isInitialUndefine = false; + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + if ( strcmp(*uit, name) == 0 ) { + isInitialUndefine = true; + break; + } + } + if ( isInitialUndefine ) + fprintf(stderr, " -u command line option\n"); + } + ++unresolvableExportsCount; + } + // be helpful and check for typos + bool printedStart = false; + for (SymbolTable::byNameIterator sit=_symbolTable.begin(); sit != _symbolTable.end(); sit++) { + const ld::Atom* atom = *sit; + if ( (atom != NULL) && (strstr(atom->name(), name) != NULL) ) { + if ( ! printedStart ) { + fprintf(stderr, " (maybe you meant: %s", atom->name()); + printedStart = true; + } + else { + fprintf(stderr, ", %s ", atom->name()); + } + } + } + if ( printedStart ) + fprintf(stderr, ")\n"); + } + } + if ( doError ) + throw "symbol(s) not found"; + } + +} + + + +void Resolver::checkDylibSymbolCollisions() +{ + for (SymbolTable::byNameIterator it=_symbolTable.begin(); it != _symbolTable.end(); it++) { + const ld::Atom* atom = *it; + if ( atom == NULL ) + continue; + if ( atom->scope() == ld::Atom::scopeGlobal ) { + // <rdar://problem/5048861> No warning about tentative definition conflicting with dylib definition + // for each tentative definition in symbol table look for dylib that exports same symbol name + if ( atom->definition() == ld::Atom::definitionTentative ) { + _inputFiles.searchLibraries(atom->name(), true, false, *this); + } + // record any overrides of weak symbols in any linked dylib + if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) ) { + if ( _inputFiles.searchWeakDefInDylib(atom->name()) ) + (const_cast<ld::Atom*>(atom))->setOverridesDylibsWeakDef(); + } + } + } +} + + +const ld::Atom* Resolver::entryPoint(bool searchArchives) +{ + const char* symbolName = NULL; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + symbolName = _options.entryName(); + break; + case Options::kDynamicLibrary: + symbolName = _options.initFunctionName(); + break; + case Options::kObjectFile: + case Options::kDynamicBundle: + case Options::kKextBundle: + return NULL; + break; + } + if ( symbolName != NULL ) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(symbolName); + if ( (_internal.indirectBindingTable[slot] == NULL) && searchArchives ) { + // <rdar://problem/7043256> ld64 can not find a -e entry point from an archive + _inputFiles.searchLibraries(symbolName, false, true, *this); + } + if ( _internal.indirectBindingTable[slot] == NULL ) { + if ( strcmp(symbolName, "start") == 0 ) + throwf("entry point (%s) undefined. Usually in crt1.o", symbolName); + else + throwf("entry point (%s) undefined.", symbolName); + } + return _internal.indirectBindingTable[slot]; + } + return NULL; +} + + +void Resolver::fillInHelpersInInternalState() +{ + // look up well known atoms + bool needsStubHelper = true; + switch ( _options.outputKind() ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + needsStubHelper = true; + break; + case Options::kDyld: + case Options::kKextBundle: + case Options::kObjectFile: + case Options::kStaticExecutable: + case Options::kPreload: + needsStubHelper = false; + break; + } + + _internal.classicBindingHelper = NULL; + if ( needsStubHelper && !_options.makeCompressedDyldInfo() ) { + // "dyld_stub_binding_helper" comes from .o file, so should already exist in symbol table + if ( _symbolTable.hasName("dyld_stub_binding_helper") ) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_stub_binding_helper"); + _internal.classicBindingHelper = _internal.indirectBindingTable[slot]; + } + } + + _internal.lazyBindingHelper = NULL; + if ( _options.usingLazyDylibLinking() ) { + // "dyld_lazy_dylib_stub_binding_helper" comes from lazydylib1.o file, so should already exist in symbol table + if ( _symbolTable.hasName("dyld_lazy_dylib_stub_binding_helper") ) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_lazy_dylib_stub_binding_helper"); + _internal.lazyBindingHelper = _internal.indirectBindingTable[slot]; + } + if ( _internal.lazyBindingHelper == NULL ) + throw "symbol dyld_lazy_dylib_stub_binding_helper not defined (usually in lazydylib1.o)"; + } + + _internal.compressedFastBinderProxy = NULL; + if ( needsStubHelper && _options.makeCompressedDyldInfo() ) { + // "dyld_stub_binder" comes from libSystem.dylib so will need to manually resolve + if ( !_symbolTable.hasName("dyld_stub_binder") ) { + _inputFiles.searchLibraries("dyld_stub_binder", true, false, *this); + } + if ( _symbolTable.hasName("dyld_stub_binder") ) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_stub_binder"); + _internal.compressedFastBinderProxy = _internal.indirectBindingTable[slot]; + } + if ( _internal.compressedFastBinderProxy == NULL ) { + if ( _options.undefinedTreatment() != Options::kUndefinedError ) { + // make proxy + _internal.compressedFastBinderProxy = new UndefinedProxyAtom("dyld_stub_binder"); + this->doAtom(*_internal.compressedFastBinderProxy); + } + else { + warning("symbol dyld_stub_binder not found, normally in libSystem.dylib"); + } + } + } +} + + +void Resolver::fillInInternalState() +{ + // store atoms into their final section + for (std::vector<const ld::Atom*>::iterator it = _atoms.begin(); it != _atoms.end(); ++it) { + _internal.addAtom(**it); + } + + // <rdar://problem/7783918> make sure there is a __text section so that codesigning works + if ( (_options.outputKind() == Options::kDynamicLibrary) || (_options.outputKind() == Options::kDynamicBundle) ) + _internal.getFinalSection(ld::Section("__TEXT", "__text", ld::Section::typeCode)); + + // add entry point + _internal.entryPoint = this->entryPoint(true); +} + + + +void Resolver::removeCoalescedAwayAtoms() +{ + _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), AtomCoalescedAway()), _atoms.end()); +} + +void Resolver::linkTimeOptimize() +{ + // only do work here if some llvm obj files where loaded + if ( ! _haveLLVMObjs ) + return; + + // run LLVM lto code-gen + lto::OptimizeOptions optOpt; + optOpt.outputFilePath = _options.outputFilePath(); + optOpt.tmpObjectFilePath = _options.tempLtoObjectPath(); + optOpt.allGlobalsAReDeadStripRoots = _options.allGlobalsAreDeadStripRoots(); + optOpt.verbose = _options.verbose(); + optOpt.saveTemps = _options.saveTempFiles(); + optOpt.pie = _options.positionIndependentExecutable(); + optOpt.mainExecutable = _options.linkingMainExecutable();; + optOpt.staticExecutable = (_options.outputKind() == Options::kStaticExecutable); + optOpt.relocatable = (_options.outputKind() == Options::kObjectFile); + optOpt.allowTextRelocs = _options.allowTextRelocs(); + optOpt.linkerDeadStripping = _options.deadCodeStrip(); + optOpt.arch = _options.architecture(); + optOpt.llvmOptions = &_options.llvmOptions(); + + std::vector<const ld::Atom*> newAtoms; + std::vector<const char*> additionalUndefines; + if ( ! lto::optimize(_atoms, _internal, _inputFiles.nextInputOrdinal(), optOpt, *this, newAtoms, additionalUndefines) ) + return; // if nothing done + + + // add all newly created atoms to _atoms and update symbol table + for(std::vector<const ld::Atom*>::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) + this->doAtom(**it); + + // some atoms might have been optimized way (marked coalesced), remove them + this->removeCoalescedAwayAtoms(); + + // add new atoms into their final section + for (std::vector<const ld::Atom*>::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) { + _internal.addAtom(**it); + } + + // remove temp lto section and move all of its atoms to their final section + ld::Internal::FinalSection* tempLTOsection = NULL; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeTempLTO ) { + tempLTOsection = sect; + // remove temp lto section from final image + _internal.sections.erase(sit); + break; + } + } + // lto atoms now have proper section info, so add to final section + if ( tempLTOsection != NULL ) { + for (std::vector<const ld::Atom*>::iterator ait=tempLTOsection->atoms.begin(); ait != tempLTOsection->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( ! atom->coalescedAway() ) { + this->convertReferencesToIndirect(*atom); + _internal.addAtom(*atom); + } + } + } + + // resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up) + _addToFinalSection = true; + for(std::vector<const char*>::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { + const char *targetName = *uit; + // these symbols may or may not already be in linker's symbol table + if ( ! _symbolTable.hasName(targetName) ) { + _inputFiles.searchLibraries(targetName, true, true, *this); + } + } + _addToFinalSection = false; + + // if -dead_strip on command line + if ( _options.deadCodeStrip() ) { + // clear liveness bit + for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + (const_cast<ld::Atom*>(*it))->setLive((*it)->dontDeadStrip()); + } + // and re-compute dead code + this->deadStripOptimize(); + + // remove newly dead atoms from each section + for (std::vector<ld::Internal::FinalSection*>::iterator sit=_internal.sections.begin(); sit != _internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), NotLive()), sect->atoms.end()); + } + } + + if ( _options.outputKind() == Options::kObjectFile ) { + // if -r mode, add proxies for new undefines (e.g. ___stack_chk_fail) + _addToFinalSection = true; + this->resolveUndefines(); + _addToFinalSection = false; + } + else { + // last chance to check for undefines + this->checkUndefines(true); + + // check new code does not override some dylib + this->checkDylibSymbolCollisions(); + } +} + + +void Resolver::resolve() +{ + this->initializeState(); + this->buildAtomList(); + this->addInitialUndefines(); + this->fillInHelpersInInternalState(); + this->resolveUndefines(); + this->deadStripOptimize(); + this->checkUndefines(); + this->checkDylibSymbolCollisions(); + this->removeCoalescedAwayAtoms(); + this->fillInInternalState(); + this->linkTimeOptimize(); +} + + + +} // namespace tool +} // namespace ld + + + diff --git a/src/ld/Resolver.h b/src/ld/Resolver.h new file mode 100644 index 0000000..1202fc7 --- /dev/null +++ b/src/ld/Resolver.h @@ -0,0 +1,148 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __RESOLVER_H__ +#define __RESOLVER_H__ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <mach/mach_time.h> +#include <mach/vm_statistics.h> +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> + +#include <vector> + +#include "Options.h" +#include "ld.hpp" +#include "SymbolTable.h" + + +namespace ld { +namespace tool { + + + + +class Resolver : public ld::File::AtomHandler +{ +public: + Resolver(const Options& opts, const InputFiles& inputs, ld::Internal& state) + : _options(opts), _inputFiles(inputs), _internal(state), + _symbolTable(opts, state.indirectBindingTable), + _haveLLVMObjs(false), _addToFinalSection(false), + _completedInitialObjectFiles(false) {} + + + virtual void doAtom(const ld::Atom&); + virtual void doFile(const class File&); + + void resolve(); + + +private: + struct WhyLiveBackChain + { + WhyLiveBackChain* previous; + const ld::Atom* referer; + }; + + void initializeState(); + void buildAtomList(); + void addInitialUndefines(); + void deadStripOptimize(); + void resolveUndefines(); + void checkUndefines(bool force=false); + void checkDylibSymbolCollisions(); + void tentativeOverrideOfDylib(ld::Atom&); + void fillInInternalState(); + void fillInHelpersInInternalState(); + void removeCoalescedAwayAtoms(); + void linkTimeOptimize(); + void convertReferencesToIndirect(const ld::Atom& atom); + const ld::Atom* entryPoint(bool searchArchives); + void markLive(const ld::Atom& atom, WhyLiveBackChain* previous); + bool isDtraceProbe(ld::Fixup::Kind kind); + void liveUndefines(std::vector<const char*>&); + static unsigned int ppcSubTypeIndex(uint32_t subtype); + bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); + + + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet; + + class NotLive { + public: + bool operator()(const ld::Atom* atom) const { + return ! (atom->live() || atom->dontDeadStrip()); + } + }; + + class AtomCoalescedAway { + public: + bool operator()(const ld::Atom* atom) const { + return atom->coalescedAway(); + } + }; + + const Options& _options; + const InputFiles& _inputFiles; + ld::Internal& _internal; + std::vector<const ld::Atom*> _atoms; + std::set<const ld::Atom*> _deadStripRoots; + std::vector<const ld::Atom*> _atomsWithUnresolvedReferences; + SymbolTable _symbolTable; + bool _haveLLVMObjs; + bool _addToFinalSection; + bool _completedInitialObjectFiles; +}; + + +class DeadStripResolver +{ +public: + + +private: + +}; + +} // namespace tool +} // namespace ld + + + +#endif // __RESOLVER_H__ diff --git a/src/ld/SymbolTable.cpp b/src/ld/SymbolTable.cpp new file mode 100644 index 0000000..f9ed04c --- /dev/null +++ b/src/ld/SymbolTable.cpp @@ -0,0 +1,721 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <assert.h> + +#include <string> +#include <map> +#include <set> +#include <vector> +#include <algorithm> +#include <ext/hash_map> +#include <ext/hash_set> + +#include "Options.h" + +#include "ld.hpp" +#include "InputFiles.h" +#include "SymbolTable.h" + + + +namespace ld { +namespace tool { + + +// HACK, I can't find a way to pass values in the compare classes (e.g. ContentFuncs) +// so use global variable to pass info. +static ld::IndirectBindingTable* _s_indirectBindingTable = NULL; +bool SymbolTable::_s_doDemangle = false; + + +SymbolTable::SymbolTable(const Options& opts, std::vector<const ld::Atom*>& ibt) + : _options(opts), _cstringTable(6151), _indirectBindingTable(ibt), _hasExternalTentativeDefinitions(false) +{ + _s_indirectBindingTable = this; + _s_doDemangle = _options.demangleSymbols(); +} + + +size_t SymbolTable::ContentFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::ContentFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + return (memcmp(left->rawContentPointer(), right->rawContentPointer(), left->size()) == 0); +} + + + +size_t SymbolTable::CStringHashFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::CStringHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + return (strcmp((char*)left->rawContentPointer(), (char*)right->rawContentPointer()) == 0); +} + + +size_t SymbolTable::UTF16StringHashFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::UTF16StringHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + if ( left == right ) + return true; + const void* leftContent = left->rawContentPointer(); + const void* rightContent = right->rawContentPointer(); + unsigned int amount = left->size()-2; + bool result = (memcmp(leftContent, rightContent, amount) == 0); + return result; +} + + +size_t SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* atom) const +{ + return atom->contentHash(*_s_indirectBindingTable); +} + +bool SymbolTable::ReferencesHashFuncs::operator()(const ld::Atom* left, const ld::Atom* right) const +{ + return left->canCoalesceWith(*right, *_s_indirectBindingTable); +} + + + +bool SymbolTable::addByName(const ld::Atom& newAtom, bool ignoreDuplicates) +{ + bool useNew = true; + bool checkVisibilityMismatch = false; + assert(newAtom.name() != NULL); + const char* name = newAtom.name(); + IndirectBindingSlot slot = this->findSlotForName(name); + const ld::Atom* existingAtom = _indirectBindingTable[slot]; + //fprintf(stderr, "addByName(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); + if ( existingAtom != NULL ) { + assert(&newAtom != existingAtom); + switch ( existingAtom->definition() ) { + case ld::Atom::definitionRegular: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + if ( existingAtom->combine() == ld::Atom::combineByName ) { + if ( newAtom.combine() == ld::Atom::combineByName ) { + // both weak, prefer non-auto-hide one + if ( newAtom.autoHide() != existingAtom->autoHide() ) { + // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden + useNew = existingAtom->autoHide(); + // don't check for visibility mismatch + } + else if ( newAtom.autoHide() && existingAtom->autoHide() ) { + // both have auto-hide, so use one with greater alignment + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + else { + // neither auto-hide, check visibility + if ( newAtom.scope() != existingAtom->scope() ) { + // <rdar://problem/8304984> use more visible weak def symbol + useNew = (newAtom.scope() == ld::Atom::scopeGlobal); + } + else { + // both have same visibility, use one with greater alignment + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + } + } + else { + // existing weak, new is not-weak + useNew = true; + } + } + else { + if ( newAtom.combine() == ld::Atom::combineByName ) { + // existing not-weak, new is weak + useNew = false; + } + else { + // existing not-weak, new is not-weak + if ( ignoreDuplicates ) { + useNew = false; + static bool fullWarning = false; + if ( ! fullWarning ) { + warning("-dead_strip with lazy loaded static (library) archives " + "has resulted in a duplicate symbol. You can change your " + "source code to rename symbols to avoid the collision. " + "This will be an error in a future linker."); + fullWarning = true; + } + warning("duplicate symbol %s originally in %s now lazily loaded from %s", + SymbolTable::demangle(name), existingAtom->file()->path(), newAtom.file()->path()); + } + else { + throwf("duplicate symbol %s in %s and %s", + SymbolTable::demangle(name), newAtom.file()->path(), existingAtom->file()->path()); + } + } + } + break; + case ld::Atom::definitionTentative: + // ignore new tentative atom, because we already have a regular one + useNew = false; + checkVisibilityMismatch = true; + if ( newAtom.size() > existingAtom->size() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "is smaller than the real definition of size %llu from %s", + newAtom.name(), newAtom.size(), newAtom.file()->path(), + existingAtom->size(), existingAtom->file()->path()); + } + break; + case ld::Atom::definitionAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + case ld::Atom::definitionProxy: + // ignore external atom, because we already have a one + useNew = false; + break; + } + break; + case ld::Atom::definitionTentative: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + // replace existing tentative atom with regular one + checkVisibilityMismatch = true; + if ( newAtom.size() < existingAtom->size() ) { + warning("for symbol %s tentative definition of size %llu from %s is " + "being replaced by a real definition of size %llu from %s", + newAtom.name(), existingAtom->size(), existingAtom->file()->path(), + newAtom.size(), newAtom.file()->path()); + } + break; + case ld::Atom::definitionTentative: + // new and existing are both tentative definitions, use largest + checkVisibilityMismatch = true; + if ( newAtom.size() < existingAtom->size() ) { + useNew = false; + } + else { + if ( newAtom.alignment().trailingZeros() < existingAtom->alignment().trailingZeros() ) + warning("alignment lost in merging tentative definition %s", newAtom.name()); + } + break; + case ld::Atom::definitionAbsolute: + // replace tentative with absolute + useNew = true; + break; + case ld::Atom::definitionProxy: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( _options.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( _options.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); + useNew = false; + break; + case Options::kCommonsOverriddenByDylibs: + if ( _options.warnCommons() ) + warning("replacing common symbol %s from %s with true definition from dylib %s", + existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + existingAtom->name(), existingAtom->file()->path(), newAtom.file()->path()); + } + break; + } + break; + case ld::Atom::definitionAbsolute: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + case ld::Atom::definitionTentative: + // ignore new tentative atom, because we already have a regular one + useNew = false; + break; + case ld::Atom::definitionAbsolute: + throwf("duplicate symbol %s in %s and %s", name, newAtom.file()->path(), existingAtom->file()->path()); + case ld::Atom::definitionProxy: + // ignore external atom, because we already have a one + useNew = false; + break; + } + break; + case ld::Atom::definitionProxy: + switch ( newAtom.definition() ) { + case ld::Atom::definitionRegular: + // replace external atom with regular one + useNew = true; + break; + case ld::Atom::definitionTentative: + // a tentative definition and a dylib definition, so commons-mode decides how to handle + switch ( _options.commonsMode() ) { + case Options::kCommonsIgnoreDylibs: + if ( _options.warnCommons() ) + warning("using common symbol %s from %s and ignoring defintion from dylib %s", + newAtom.name(), newAtom.file()->path(), existingAtom->file()->path()); + break; + case Options::kCommonsOverriddenByDylibs: + if ( _options.warnCommons() ) + warning("replacing defintion of %s from dylib %s with common symbol from %s", + newAtom.name(), existingAtom->file()->path(), newAtom.file()->path()); + useNew = false; + break; + case Options::kCommonsConflictsDylibsError: + throwf("common symbol %s from %s conflicts with defintion from dylib %s", + newAtom.name(), newAtom.file()->path(), existingAtom->file()->path()); + } + break; + case ld::Atom::definitionAbsolute: + // replace external atom with absolute one + useNew = true; + break; + case ld::Atom::definitionProxy: + // <rdar://problem/5137732> ld should keep looking when it finds a weak definition in a dylib + if ( newAtom.combine() == ld::Atom::combineByName ) { + useNew = false; + } + else { + if ( existingAtom->combine() == ld::Atom::combineByName ) + useNew = true; + else + throwf("symbol %s exported from both %s and %s\n", name, newAtom.file()->path(), existingAtom->file()->path()); + } + break; + } + break; + } + } + if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.scope() != existingAtom->scope()) ) { + warning("%s has different visibility (%s) in %s and (%s) in %s", + SymbolTable::demangle(newAtom.name()), (newAtom.scope() == 1 ? "hidden" : "default"), newAtom.file()->path(), (existingAtom->scope() == 1 ? "hidden" : "default"), existingAtom->file()->path()); + } + if ( useNew ) { + _indirectBindingTable[slot] = &newAtom; + if ( existingAtom != NULL ) { + markCoalescedAway(existingAtom); +// if ( fOwner.fInitialLoadsDone ) { +// //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->name(), &newAtom); +// fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom); +// } + } + if ( newAtom.scope() == ld::Atom::scopeGlobal ) { + if ( newAtom.definition() == ld::Atom::definitionTentative ) { + _hasExternalTentativeDefinitions = true; + } + } + } + else { + markCoalescedAway(&newAtom); + } + // return if existing atom in symbol table was replaced + return useNew && (existingAtom != NULL); +} + + +bool SymbolTable::addByContent(const ld::Atom& newAtom) +{ + bool useNew = true; + const ld::Atom* existingAtom; + IndirectBindingSlot slot = this->findSlotForContent(&newAtom, &existingAtom); + //fprintf(stderr, "addByContent(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); + if ( existingAtom != NULL ) { + // use existing unless new one has greater alignment requirements + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + if ( useNew ) { + _indirectBindingTable[slot] = &newAtom; + if ( existingAtom != NULL ) + markCoalescedAway(existingAtom); + } + else { + _indirectBindingTable[slot] = existingAtom; + if ( existingAtom != &newAtom ) + markCoalescedAway(&newAtom); + } + // return if existing atom in symbol table was replaced + return useNew && (existingAtom != NULL); +} + +bool SymbolTable::addByReferences(const ld::Atom& newAtom) +{ + bool useNew = true; + const ld::Atom* existingAtom; + IndirectBindingSlot slot = this->findSlotForReferences(&newAtom, &existingAtom); + //fprintf(stderr, "addByReferences(%p) name=%s, slot=%u, existing=%p\n", &newAtom, newAtom.name(), slot, existingAtom); + if ( existingAtom != NULL ) { + // use existing unless new one has greater alignment requirements + useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() ); + } + if ( useNew ) { + _indirectBindingTable[slot] = &newAtom; + if ( existingAtom != NULL ) + markCoalescedAway(existingAtom); + } + else { + if ( existingAtom != &newAtom ) + markCoalescedAway(&newAtom); + } + // return if existing atom in symbol table was replaced + return useNew && (existingAtom != NULL); +} + + +bool SymbolTable::add(const ld::Atom& atom, bool ignoreDuplicates) +{ + //fprintf(stderr, "SymbolTable::add(%p), name=%s\n", &atom, atom.name()); + assert(atom.scope() != ld::Atom::scopeTranslationUnit); + switch ( atom.combine() ) { + case ld::Atom::combineNever: + case ld::Atom::combineByName: + return this->addByName(atom, ignoreDuplicates); + break; + case ld::Atom::combineByNameAndContent: + return this->addByContent(atom); + break; + case ld::Atom::combineByNameAndReferences: + return this->addByReferences(atom); + break; + } + + return false; +} + +void SymbolTable::markCoalescedAway(const ld::Atom* atom) +{ + // remove this from list of all atoms used + //fprintf(stderr, "markCoalescedAway(%p) from %s\n", atom, atom->file()->path()); + (const_cast<ld::Atom*>(atom))->setCoalescedAway(); + + // + // The fixupNoneGroupSubordinate* fixup kind is used to model group comdat. + // The "signature" atom in the group has a fixupNoneGroupSubordinate* fixup to + // all other members of the group. So, if the signature atom is + // coalesced away, all other atoms in the group should also be removed. + // + for (ld::Fixup::iterator fit=atom->fixupsBegin(), fend=atom->fixupsEnd(); fit != fend; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + this->markCoalescedAway(fit->u.target); + break; + default: + break; + } + } + +} + +void SymbolTable::undefines(std::vector<const char*>& undefs) +{ + // return all names in _byNameTable that have no associated atom + for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { + //fprintf(stderr, " _byNameTable[%s] = slot %d which has atom %p\n", it->first, it->second, _indirectBindingTable[it->second]); + if ( _indirectBindingTable[it->second] == NULL ) + undefs.push_back(it->first); + } + // sort so that undefines are in a stable order (not dependent on hashing functions) + std::sort(undefs.begin(), undefs.end()); +} + + +void SymbolTable::tentativeDefs(std::vector<const char*>& tents) +{ + // return all names in _byNameTable that have no associated atom + for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { + const char* name = it->first; + const ld::Atom* atom = _indirectBindingTable[it->second]; + if ( (atom != NULL) && (atom->definition() == ld::Atom::definitionTentative) ) + tents.push_back(name); + } + std::sort(tents.begin(), tents.end()); +} + + +bool SymbolTable::hasName(const char* name) +{ + NameToSlot::iterator pos = _byNameTable.find(name); + if ( pos == _byNameTable.end() ) + return false; + return (_indirectBindingTable[pos->second] != NULL); +} + +// find existing or create new slot +SymbolTable::IndirectBindingSlot SymbolTable::findSlotForName(const char* name) +{ + NameToSlot::iterator pos = _byNameTable.find(name); + if ( pos != _byNameTable.end() ) + return pos->second; + // create new slot for this name + SymbolTable::IndirectBindingSlot slot = _indirectBindingTable.size(); + _indirectBindingTable.push_back(NULL); + _byNameTable[name] = slot; + _byNameReverseTable[slot] = name; + return slot; +} + + +// find existing or create new slot +SymbolTable::IndirectBindingSlot SymbolTable::findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom) +{ + //fprintf(stderr, "findSlotForContent(%p)\n", atom); + SymbolTable::IndirectBindingSlot slot = 0; + UTF16StringToSlot::iterator upos; + CStringToSlot::iterator cspos; + ContentToSlot::iterator pos; + switch ( atom->section().type() ) { + case ld::Section::typeCString: + cspos = _cstringTable.find(atom); + if ( cspos != _cstringTable.end() ) { + *existingAtom = _indirectBindingTable[cspos->second]; + return cspos->second; + } + slot = _indirectBindingTable.size(); + _cstringTable[atom] = slot; + break; + case ld::Section::typeNonStdCString: + { + // use seg/sect name is key to map to avoid coalescing across segments and sections + char segsect[64]; + sprintf(segsect, "%s/%s", atom->section().segmentName(), atom->section().sectionName()); + NameToMap::iterator mpos = _nonStdCStringSectionToMap.find(segsect); + CStringToSlot* map = NULL; + if ( mpos == _nonStdCStringSectionToMap.end() ) { + map = new CStringToSlot(); + _nonStdCStringSectionToMap[strdup(segsect)] = map; + } + else { + map = mpos->second; + } + cspos = map->find(atom); + if ( cspos != map->end() ) { + *existingAtom = _indirectBindingTable[cspos->second]; + return cspos->second; + } + slot = _indirectBindingTable.size(); + map->operator[](atom) = slot; + } + break; + case ld::Section::typeUTF16Strings: + upos = _utf16Table.find(atom); + if ( upos != _utf16Table.end() ) { + *existingAtom = _indirectBindingTable[upos->second]; + return upos->second; + } + slot = _indirectBindingTable.size(); + _utf16Table[atom] = slot; + break; + case ld::Section::typeLiteral4: + pos = _literal4Table.find(atom); + if ( pos != _literal4Table.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _literal4Table[atom] = slot; + break; + case ld::Section::typeLiteral8: + pos = _literal8Table.find(atom); + if ( pos != _literal8Table.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _literal8Table[atom] = slot; + break; + case ld::Section::typeLiteral16: + pos = _literal16Table.find(atom); + if ( pos != _literal16Table.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _literal16Table[atom] = slot; + break; + default: + assert(0 && "section type does not support coalescing by content"); + } + _indirectBindingTable.push_back(atom); + *existingAtom = NULL; + return slot; +} + + + +// find existing or create new slot +SymbolTable::IndirectBindingSlot SymbolTable::findSlotForReferences(const ld::Atom* atom, const ld::Atom** existingAtom) +{ + //fprintf(stderr, "findSlotForReferences(%p)\n", atom); + + SymbolTable::IndirectBindingSlot slot = 0; + ReferencesToSlot::iterator pos; + switch ( atom->section().type() ) { + case ld::Section::typeNonLazyPointer: + pos = _nonLazyPointerTable.find(atom); + if ( pos != _nonLazyPointerTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _nonLazyPointerTable[atom] = slot; + break; + case ld::Section::typeCFString: + pos = _cfStringTable.find(atom); + if ( pos != _cfStringTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _cfStringTable[atom] = slot; + break; + case ld::Section::typeObjCClassRefs: + pos = _objc2ClassRefTable.find(atom); + if ( pos != _objc2ClassRefTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _objc2ClassRefTable[atom] = slot; + break; + case ld::Section::typeCStringPointer: + pos = _pointerToCStringTable.find(atom); + if ( pos != _pointerToCStringTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _pointerToCStringTable[atom] = slot; + break; + default: + assert(0 && "section type does not support coalescing by references"); + } + _indirectBindingTable.push_back(atom); + *existingAtom = NULL; + return slot; +} + + +const char* SymbolTable::indirectName(IndirectBindingSlot slot) const +{ + assert(slot < _indirectBindingTable.size()); + const ld::Atom* target = _indirectBindingTable[slot]; + if ( target != NULL ) { + return target->name(); + } + // handle case when by-name reference is indirected and no atom yet in _byNameTable + SlotToName::const_iterator pos = _byNameReverseTable.find(slot); + if ( pos != _byNameReverseTable.end() ) + return pos->second; + assert(0); + return NULL; +} + +const ld::Atom* SymbolTable::indirectAtom(IndirectBindingSlot slot) const +{ + assert(slot < _indirectBindingTable.size()); + return _indirectBindingTable[slot]; +} + +extern "C" char* __cxa_demangle (const char* mangled_name, + char* buf, + size_t* n, + int* status); + +const char* SymbolTable::demangle(const char* sym) +{ + // only try to demangle symbols if -demangle on command line + if ( !_s_doDemangle ) + return sym; + + // only try to demangle symbols that look like C++ symbols + if ( strncmp(sym, "__Z", 3) != 0 ) + return sym; + + static size_t size = 1024; + static char* buff = (char*)malloc(size); + int status; + + char* result = __cxa_demangle(&sym[1], buff, &size, &status); + if ( result != NULL ) { + // if demangling succesful, keep buffer for next demangle + buff = result; + return buff; + } + return sym; +} + + +void SymbolTable::printStatistics() +{ +// fprintf(stderr, "cstring table size: %lu, bucket count: %lu, hash func called %u times\n", +// _cstringTable.size(), _cstringTable.bucket_count(), cstringHashCount); + int count[11]; + for(unsigned int b=0; b < 11; ++b) { + count[b] = 0; + } + for(unsigned int i=0; i < _cstringTable.bucket_count(); ++i) { + unsigned int n = _cstringTable.elems_in_bucket(i); + if ( n < 10 ) + count[n] += 1; + else + count[10] += 1; + } + fprintf(stderr, "cstring table distribution\n"); + for(unsigned int b=0; b < 11; ++b) { + fprintf(stderr, "%u buckets have %u elements\n", count[b], b); + } + fprintf(stderr, "indirect table size: %lu\n", _indirectBindingTable.size()); + fprintf(stderr, "by-name table size: %lu\n", _byNameTable.size()); +// fprintf(stderr, "by-content table size: %lu, hash count: %u, equals count: %u, lookup count: %u\n", +// _byContentTable.size(), contentHashCount, contentEqualCount, contentLookupCount); +// fprintf(stderr, "by-ref table size: %lu, hashed count: %u, equals count: %u, lookup count: %u, insert count: %u\n", +// _byReferencesTable.size(), refHashCount, refEqualsCount, refLookupCount, refInsertCount); + + //ReferencesHash obj; + //for(ReferencesHashToSlot::iterator it=_byReferencesTable.begin(); it != _byReferencesTable.end(); ++it) { + // if ( obj.operator()(it->first) == 0x2F3AC0EAC744EA70 ) { + // fprintf(stderr, "hash=0x2F3AC0EAC744EA70 for %p %s from %s\n", it->first, it->first->name(), it->first->file()->path()); + // + // } + //} + +} + +} // namespace tool +} // namespace ld + diff --git a/src/ld/SymbolTable.h b/src/ld/SymbolTable.h new file mode 100644 index 0000000..f352a51 --- /dev/null +++ b/src/ld/SymbolTable.h @@ -0,0 +1,165 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __SYMBOL_TABLE_H__ +#define __SYMBOL_TABLE_H__ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <mach/mach_time.h> +#include <mach/vm_statistics.h> +#include <mach/mach_init.h> +#include <mach/mach_host.h> +#include <dlfcn.h> +#include <mach-o/dyld.h> + +#include <vector> +#include <ext/hash_map> + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace tool { + + +class SymbolTable : public ld::IndirectBindingTable +{ +public: + typedef uint32_t IndirectBindingSlot; + +private: + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map<const char*, IndirectBindingSlot, __gnu_cxx::hash<const char*>, CStringEquals> NameToSlot; + + class ContentFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map<const ld::Atom*, IndirectBindingSlot, ContentFuncs, ContentFuncs> ContentToSlot; + + class ReferencesHashFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map<const ld::Atom*, IndirectBindingSlot, ReferencesHashFuncs, ReferencesHashFuncs> ReferencesToSlot; + + class CStringHashFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map<const ld::Atom*, IndirectBindingSlot, CStringHashFuncs, CStringHashFuncs> CStringToSlot; + + class UTF16StringHashFuncs { + public: + size_t operator()(const ld::Atom*) const; + bool operator()(const ld::Atom* left, const ld::Atom* right) const; + }; + typedef __gnu_cxx::hash_map<const ld::Atom*, IndirectBindingSlot, UTF16StringHashFuncs, UTF16StringHashFuncs> UTF16StringToSlot; + + typedef std::map<IndirectBindingSlot, const char*> SlotToName; + typedef __gnu_cxx::hash_map<const char*, CStringToSlot*, __gnu_cxx::hash<const char*>, CStringEquals> NameToMap; + +public: + + class byNameIterator { + public: + byNameIterator& operator++(int) { ++_nameTableIterator; return *this; } + const ld::Atom* operator*() { return _slotTable[_nameTableIterator->second]; } + bool operator!=(const byNameIterator& lhs) { return _nameTableIterator != lhs._nameTableIterator; } + + private: + friend class SymbolTable; + byNameIterator(NameToSlot::iterator it, std::vector<const ld::Atom*>& indirectTable) + : _nameTableIterator(it), _slotTable(indirectTable) {} + + NameToSlot::iterator _nameTableIterator; + std::vector<const ld::Atom*>& _slotTable; + }; + + SymbolTable(const Options& opts, std::vector<const ld::Atom*>& ibt); + + bool add(const ld::Atom& atom, bool ignoreDuplicates); + IndirectBindingSlot findSlotForName(const char* name); + IndirectBindingSlot findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom); + IndirectBindingSlot findSlotForReferences(const ld::Atom* atom, const ld::Atom** existingAtom); + const ld::Atom* atomForSlot(IndirectBindingSlot s) { return _indirectBindingTable[s]; } + unsigned int updateCount() { return _indirectBindingTable.size(); } + void undefines(std::vector<const char*>& undefines); + void tentativeDefs(std::vector<const char*>& undefines); + bool hasName(const char* name); + bool hasExternalTentativeDefinitions() { return _hasExternalTentativeDefinitions; } + byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); } + byNameIterator end() { return byNameIterator(_byNameTable.end(),_indirectBindingTable); } + void printStatistics(); + static const char* demangle(const char* sym); + + // from ld::IndirectBindingTable + virtual const char* indirectName(IndirectBindingSlot slot) const; + virtual const ld::Atom* indirectAtom(IndirectBindingSlot slot) const; + +private: + bool addByName(const ld::Atom& atom, bool ignoreDuplicates); + bool addByContent(const ld::Atom& atom); + bool addByReferences(const ld::Atom& atom); + void markCoalescedAway(const ld::Atom* atom); + + const Options& _options; + NameToSlot _byNameTable; + SlotToName _byNameReverseTable; + ContentToSlot _literal4Table; + ContentToSlot _literal8Table; + ContentToSlot _literal16Table; + UTF16StringToSlot _utf16Table; + CStringToSlot _cstringTable; + NameToMap _nonStdCStringSectionToMap; + ReferencesToSlot _nonLazyPointerTable; + ReferencesToSlot _cfStringTable; + ReferencesToSlot _objc2ClassRefTable; + ReferencesToSlot _pointerToCStringTable; + std::vector<const ld::Atom*>& _indirectBindingTable; + bool _hasExternalTentativeDefinitions; + + static bool _s_doDemangle; + +}; + +} // namespace tool +} // namespace ld + + +#endif // __SYMBOL_TABLE_H__ diff --git a/src/ld/debugline.c b/src/ld/debugline.c index 971e616..c367d00 100644 --- a/src/ld/debugline.c +++ b/src/ld/debugline.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -345,6 +345,8 @@ line_at_eof (struct line_reader_data * lnd) return lnd->cpos == lnd->end; } +static const bool verbose = false; + static bool next_state (struct line_reader_data *lnd) { @@ -373,28 +375,34 @@ next_state (struct line_reader_data *lnd) case DW_LNS_extended_op: { uint64_t sz = read_uleb128 (lnd); - const uint8_t * op = lnd->cpos; + const uint8_t * eop = lnd->cpos; - if ((uint64_t)(lnd->end - op) < sz || sz == 0) + if ((uint64_t)(lnd->end - eop) < sz || sz == 0) return false; lnd->cpos += sz; - switch (*op++) + switch (*eop++) { case DW_LNE_end_sequence: + if (verbose) fprintf(stderr, "DW_LNE_end_sequence\n"); lnd->cur.end_of_sequence = true; return true; case DW_LNE_set_address: - if (sz == 9) - lnd->cur.pc = read_64 (op); - else if (sz == 5) - lnd->cur.pc = read_32 (op); + if (sz == 9) { + lnd->cur.pc = read_64 (eop); + if (verbose) fprintf(stderr, "DW_LNE_set_address(0x%08llX)\n", lnd->cur.pc); + } + else if (sz == 5) { + lnd->cur.pc = read_32 (eop); + if (verbose) fprintf(stderr, "DW_LNE_set_address(0x%08llX)\n", lnd->cur.pc); + } else return false; break; case DW_LNE_define_file: { + if (verbose) fprintf(stderr, "DW_LNE_define_file\n"); const uint8_t * * filenames; filenames = realloc (lnd->filenames, @@ -402,9 +410,9 @@ next_state (struct line_reader_data *lnd) if (! filenames) return false; /* Check for zero-termination. */ - if (! memchr (op, 0, lnd->cpos - op)) + if (! memchr (eop, 0, lnd->cpos - eop)) return false; - filenames[lnd->numfile++] = op; + filenames[lnd->numfile++] = eop; lnd->filenames = filenames; /* There's other data here, like file sizes and modification @@ -414,40 +422,41 @@ next_state (struct line_reader_data *lnd) default: /* Don't understand it, so skip it. */ + if (verbose) fprintf(stderr, "DW_LNS_extended_op unknown\n"); break; } break; } case DW_LNS_copy: - //fprintf(stderr, "DW_LNS_copy\n"); + if (verbose) fprintf(stderr, "DW_LNS_copy\n"); return true; case DW_LNS_advance_pc: - //fprintf(stderr, "DW_LNS_advance_pc\n"); tmp = read_uleb128 (lnd); if (tmp == (uint64_t) -1) return false; lnd->cur.pc += tmp * lnd->minimum_instruction_length; + if (verbose) fprintf(stderr, "DW_LNS_advance_pc(0x%08llX)\n", lnd->cur.pc); break; case DW_LNS_advance_line: - //fprintf(stderr, "DW_LNS_advance_line\n"); lnd->cur.line += read_sleb128 (lnd); + if (verbose) fprintf(stderr, "DW_LNS_advance_line(%lld)\n", lnd->cur.line); break; case DW_LNS_set_file: - //fprintf(stderr, "DW_LNS_set_file\n"); + if (verbose) fprintf(stderr, "DW_LNS_set_file\n"); lnd->cur.file = read_uleb128 (lnd); break; case DW_LNS_set_column: - //fprintf(stderr, "DW_LNS_set_column\n"); + if (verbose) fprintf(stderr, "DW_LNS_set_column\n"); lnd->cur.col = read_uleb128 (lnd); break; case DW_LNS_const_add_pc: - //fprintf(stderr, "DW_LNS_const_add_pc\n"); + if (verbose) fprintf(stderr, "DW_LNS_const_add_pc\n"); lnd->cur.pc += ((255 - lnd->opcode_base) / lnd->line_range * lnd->minimum_instruction_length); break; case DW_LNS_fixed_advance_pc: - //fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); + if (verbose) fprintf(stderr, "DW_LNS_fixed_advance_pc\n"); if (lnd->end - lnd->cpos < 2) return false; lnd->cur.pc += read_16 (lnd->cpos); @@ -456,6 +465,7 @@ next_state (struct line_reader_data *lnd) default: { /* Don't know what it is, so skip it. */ + if (verbose) fprintf(stderr, "unknown opcode\n"); int i; for (i = 0; i < lnd->standard_opcode_lengths[op - 1]; i++) skip_leb128 (lnd); diff --git a/src/ld/debugline.h b/src/ld/debugline.h index 51d585e..e45c290 100644 --- a/src/ld/debugline.h +++ b/src/ld/debugline.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006 Apple, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -20,6 +20,9 @@ * * @APPLE_LICENSE_HEADER_END@ */ +#ifndef __DEBUG_LINE_H__ +#define __DEBUG_LINE_H__ + #include <stdint.h> #include <stddef.h> @@ -107,3 +110,5 @@ void line_free (struct line_reader_data * lnd); } #endif +#endif // __DEBUG_LINE_H__ + diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index 115240e..d9d66a6 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -1,5 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* - * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,7 +24,7 @@ // start temp HACK for cross builds extern "C" double log2 ( double ); -#define __MATH__ +//#define __MATH__ // end temp HACK for cross builds @@ -36,11 +37,15 @@ extern "C" double log2 ( double ); #include <errno.h> #include <limits.h> #include <unistd.h> +#include <execinfo.h> #include <mach/mach_time.h> #include <mach/vm_statistics.h> #include <mach/mach_init.h> #include <mach/mach_host.h> #include <dlfcn.h> +#include <mach-o/dyld.h> +#include <dlfcn.h> +#include <AvailabilityMacros.h> #include <string> #include <map> @@ -50,848 +55,495 @@ extern "C" double log2 ( double ); #include <list> #include <algorithm> #include <ext/hash_map> -#include <dlfcn.h> -#include <AvailabilityMacros.h> +#include <ext/hash_set> +#include <cxxabi.h> -#include "configure.h" #include "Options.h" -#include "ObjectFile.h" - -#include "MachOReaderRelocatable.hpp" -#include "ArchiveReader.hpp" -#include "MachOReaderDylib.hpp" -#include "MachOWriterExecutable.hpp" - - -#if LTO_SUPPORT -#include "LTOReader.hpp" -#endif +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ld.hpp" -#include "OpaqueSection.hpp" +#include "InputFiles.h" +#include "Resolver.h" +#include "OutputFile.h" +#include "passes/stubs/make_stubs.h" +#include "passes/dtrace_dof.h" +#include "passes/got.h" +#include "passes/tlvp.h" +#include "passes/huge.h" +#include "passes/compact_unwind.h" +#include "passes/order_file.h" +#include "passes/branch_island.h" +#include "passes/branch_shim.h" +#include "passes/objc.h" +#include "passes/dylibs.h" -class CStringComparor -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) < 0); } -}; +#include "parsers/archive_file.h" +#include "parsers/macho_relocatable_file.h" +#include "parsers/macho_dylib_file.h" +#include "parsers/lto_file.h" +#include "parsers/opaque_section_file.h" -class CStringEquals -{ -public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; -class Section : public ObjectFile::Section +class InternalState : public ld::Internal { public: - static Section* find(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill, bool createIfNeeded=true); - static void assignIndexes(bool objfile); - const char* getName() { return fSectionName; } + InternalState(const Options& opts) : _options(opts) { } + virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom); + virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&); + + void sortSections(); + virtual ~InternalState() {} private: - Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill); - struct Sorter { - static int segmentOrdinal(const char* segName); - bool operator()(Section* left, Section* right); + class FinalSection : public ld::Internal::FinalSection + { + public: + FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile); + static int sectionComparer(const void* l, const void* r); + static const ld::Section& outputSection(const ld::Section& sect); + static const ld::Section& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal); + private: + friend class InternalState; + static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen); + static uint32_t segmentOrder(const ld::Section& sect, bool objFile); + uint32_t _segmentOrder; + uint32_t _sectionOrder; + + static std::vector<const char*> _s_segmentsSeen; + static ld::Section _s_DATA_data; + static ld::Section _s_DATA_const; + static ld::Section _s_TEXT_text; + static ld::Section _s_TEXT_const; + static ld::Section _s_DATA_nl_symbol_ptr; + static ld::Section _s_DATA_common; }; - - typedef __gnu_cxx::hash_map<const char*, uint32_t, __gnu_cxx::hash<const char*>, CStringEquals> NameToOrdinal; - typedef __gnu_cxx::hash_map<const char*, class Section*, __gnu_cxx::hash<const char*>, CStringEquals> NameToSection; - //typedef std::map<const char*, class Section*, CStringComparor> NameToSection; - - char fSectionName[18]; - char fSegmentName[18]; - bool fZeroFill; - bool fUntrustedZeroFill; - - static NameToSection fgMapping; - static std::vector<Section*> fgSections; - static NameToOrdinal fgSegmentDiscoverOrder; - static bool fgMakingObjectFile; -}; - -Section::NameToSection Section::fgMapping; -std::vector<Section*> Section::fgSections; -Section::NameToOrdinal Section::fgSegmentDiscoverOrder; -bool Section::fgMakingObjectFile; - -Section::Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill) - : fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill) -{ - strlcpy(fSectionName, sectionName, sizeof(fSectionName)); - strlcpy(fSegmentName, segmentName, sizeof(fSegmentName)); - - this->fIndex = fgSections.size() + 20; // room for 20 standard sections - // special placement of some sections - if ( strcmp(segmentName, "__TEXT") == 0 ) { - // sort mach header and load commands to start of TEXT - if ( strcmp(sectionName, "._mach_header") == 0 ) - this->fIndex = 1; - else if ( strcmp(sectionName, "._load_commands") == 0 ) - this->fIndex = 2; - else if ( strcmp(sectionName, "._load_cmds_pad") == 0 ) - this->fIndex = 3; - // sort __text after load commands - else if ( strcmp(sectionName, "__text") == 0 ) - this->fIndex = 10; - // sort arm/ppc stubs after text to make branch islands feasible - else if ( strcmp(sectionName, "__picsymbolstub4") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__symbol_stub4") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__picsymbolstub1") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__symbol_stub1") == 0 ) - this->fIndex = 11; - // sort fast arm stubs to end of __TEXT to be close to lazy pointers - else if ( strcmp(sectionName, "__symbolstub1") == 0 ) - this->fIndex = INT_MAX; - // sort unwind info to end of segment - else if ( strcmp(sectionName, "__eh_frame") == 0 ) - this->fIndex = INT_MAX-1; - else if ( strcmp(sectionName, "__unwind_info") == 0 ) - this->fIndex = INT_MAX-2; - else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) - this->fIndex = INT_MAX-3; - } - else if ( strcmp(segmentName, "__DATA") == 0 ) { - // sort arm lazy symbol pointers that must be at start of __DATA - if ( strcmp(sectionName, "__lazy_symbol") == 0 ) - this->fIndex = 0; - // sort sections dyld will touch to start of segment - else if ( strcmp(sectionName, "__dyld") == 0 ) - this->fIndex = 1; - else if ( strcmp(sectionName, "__program_vars") == 0 ) - this->fIndex = 1; - else if ( strcmp(sectionName, "__mod_init_func") == 0 ) - this->fIndex = 2; - else if ( strcmp(sectionName, "__nl_symbol_ptr") == 0 ) - this->fIndex = 3; - else if ( strcmp(sectionName, "__la_symbol_ptr") == 0 ) - this->fIndex = 4; - else if ( strcmp(sectionName, "__const") == 0 ) - this->fIndex = 5; - else if ( strcmp(sectionName, "__cfstring") == 0 ) - this->fIndex = 6; - else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) - this->fIndex = 7; - else if ( strcmp(sectionName, "__objc_data") == 0 ) - this->fIndex = 8; - else if ( strcmp(sectionName, "__objc_msgrefs") == 0 ) - this->fIndex = 9; - else if ( strcmp(sectionName, "__objc_protorefs") == 0 ) - this->fIndex = 10; - else if ( strcmp(sectionName, "__objc_selrefs") == 0 ) - this->fIndex = 11; - else if ( strcmp(sectionName, "__objc_classrefs") == 0 ) - this->fIndex = 12; - else if ( strcmp(sectionName, "__objc_superrefs") == 0 ) - this->fIndex = 13; - else if ( strcmp(sectionName, "__objc_const") == 0 ) - this->fIndex = 14; - else if ( strcmp(sectionName, "__objc_classlist") == 0 ) - this->fIndex = 15; - else if ( strcmp(sectionName, "__objc_nlclslist") == 0 ) - this->fIndex = 16; - else if ( strcmp(sectionName, "__objc_catlist") == 0 ) - this->fIndex = 17; - else if ( strcmp(sectionName, "__objc_protolist") == 0 ) - this->fIndex = 18; - else if ( strcmp(sectionName, "__objc_imageinfo") == 0 ) - this->fIndex = 19; - else if ( strcmp(sectionName, "__huge") == 0 ) - this->fIndex = INT_MAX; - } - //fprintf(stderr, "new Section(%s, %s) => %p, %u\n", sectionName, segmentName, this, this->getIndex()); -} - -Section* Section::find(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill, bool createIfNeeded) -{ - NameToSection::iterator pos = fgMapping.find(sectionName); - if ( pos != fgMapping.end() ) { - if ( strcmp(pos->second->fSegmentName, segmentName) == 0 ) { - if ( !untrustedZeroFill && pos->second->fUntrustedZeroFill ) { - pos->second->fZeroFill = zeroFill; - pos->second->fUntrustedZeroFill = false; - } - return pos->second; - } - // otherwise same section name is used in different segments, look slow way - for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - if ( (strcmp((*it)->fSectionName, sectionName) == 0) && (strcmp((*it)->fSegmentName, segmentName) == 0) ) - return *it; - } - } - - if ( !createIfNeeded ) - return NULL; + struct SectionHash { + size_t operator()(const ld::Section*) const; + }; + struct SectionEquals { + bool operator()(const ld::Section* left, const ld::Section* right) const; + }; + typedef __gnu_cxx::hash_map<const ld::Section*, FinalSection*, SectionHash, SectionEquals> SectionInToOut; - // does not exist, so make a new one - Section* sect = new Section(sectionName, segmentName, zeroFill, untrustedZeroFill); - fgMapping[sectionName] = sect; - fgSections.push_back(sect); - if ( (strcmp(sectionName, "__text") == 0) && (strcmp(segmentName, "__TEXT") == 0) ) { - // special case __StaticInit to be right after __text - find("__StaticInit", "__TEXT", false, true); - } + SectionInToOut _sectionInToFinalMap; + const Options& _options; +}; - // remember segment discovery order - if ( fgSegmentDiscoverOrder.find(segmentName) == fgSegmentDiscoverOrder.end() ) - fgSegmentDiscoverOrder[segmentName] = fgSegmentDiscoverOrder.size(); +ld::Section InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_DATA_const("__DATA", "__const", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_TEXT_text( "__TEXT", "__text", ld::Section::typeCode); +ld::Section InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); +ld::Section InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill); +std::vector<const char*> InternalState::FinalSection::_s_segmentsSeen; - return sect; -} -int Section::Sorter::segmentOrdinal(const char* segName) +size_t InternalState::SectionHash::operator()(const ld::Section* sect) const { - if ( strcmp(segName, "__HEADER") == 0 ) - return 1; - if ( strcmp(segName, "__PAGEZERO") == 0 ) - return 1; - if ( strcmp(segName, "__TEXT") == 0 ) - return 2; - if ( strcmp(segName, "__DATA") == 0 ) - return (fgMakingObjectFile ? 6 : 3); // __DATA is last in .o files and here in FLI - if ( strcmp(segName, "__OBJC") == 0 ) - return 4; - if ( strcmp(segName, "__IMPORT") == 0 ) - return 5; - if ( strcmp(segName, "__LINKEDIT") == 0 ) - return INT_MAX; // linkedit segment should always sort last - else - return fgSegmentDiscoverOrder[segName]+6; + size_t hash = 0; + __gnu_cxx::hash<const char*> temp; + hash += temp.operator()(sect->segmentName()); + hash += temp.operator()(sect->sectionName()); + return hash; } - -bool Section::Sorter::operator()(Section* left, Section* right) +bool InternalState::SectionEquals::operator()(const ld::Section* left, const ld::Section* right) const { - // Segment is primary sort key - int leftSegOrdinal = segmentOrdinal(left->fSegmentName); - int rightSegOrdinal = segmentOrdinal(right->fSegmentName); - if ( leftSegOrdinal < rightSegOrdinal ) - return true; - if ( leftSegOrdinal > rightSegOrdinal ) - return false; - - // zerofill section sort to the end - if ( !left->fZeroFill && right->fZeroFill ) - return true; - if ( left->fZeroFill && !right->fZeroFill ) - return false; - - // section discovery order is last sort key - return left->fIndex < right->fIndex; + return (*left == *right); } -void Section::assignIndexes(bool objfile) -{ - //printf("unsorted sections:\n"); - //for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: name=%s, segment: name=%s, discovery order=%d\n", (*it)->fSectionName, (*it)->fSegmentName, (*it)->fIndex); - //} - - // sort it - Section::fgMakingObjectFile = objfile; - std::sort(fgSections.begin(), fgSections.end(), Section::Sorter()); - // assign correct section ordering to each Section object - unsigned int newOrder = 1; - for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) - (*it)->fIndex = newOrder++; - - //printf("sorted sections:\n"); - //for (std::vector<Section*>::iterator it=fgSections.begin(); it != fgSections.end(); it++) { - // printf("section: index=%d, obj=%p, name=%s\n", (*it)->fIndex, (*it), (*it)->fSectionName); - //} +InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile) + : ld::Internal::FinalSection(sect), + _segmentOrder(segmentOrder(sect, objFile)), + _sectionOrder(sectionOrder(sect, sectionsSeen)) +{ + //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n", + // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder); } -class Linker : public ObjectFile::Reader::DylibHander { -public: - Linker(int argc, const char* argv[]); - - const char* getArchPrefix(); - const char* architectureName(); - bool showArchitectureInErrors(); - bool isInferredArchitecture(); - void createReaders(); - void createWriter(); - void addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& ); - void setOutputFile(ExecutableFile::Writer* writer); - void link(); - void optimize(); - - // implemenation from ObjectFile::Reader::DylibHander - virtual ObjectFile::Reader* findDylib(const char* installPath, const char* fromPath); - -private: - struct WhyLiveBackChain - { - WhyLiveBackChain* previous; - ObjectFile::Atom* referer; - }; - - ObjectFile::Reader* createReader(const Options::FileInfo&); - const char* fileArch(const void* p); - void addAtom(ObjectFile::Atom& atom); - void addAtoms(std::vector<class ObjectFile::Atom*>& atoms); - void buildAtomList(); - void adjustScope(); - void processDylibs(); - void markDead(ObjectFile::Atom* atom); - void updateConstraints(ObjectFile::Reader* reader); - void loadAndResolve(); - void processDTrace(); - void checkObjC(); - void addSynthesizedAtoms(); - void loadUndefines(); - void checkUndefines(); - void resolveReferences(); - void deadStripResolve(); - void addLiveRoot(const char* name); - void moveToFrontOfSection(ObjectFile::Atom* atom); - ObjectFile::Atom* findAtom(const Options::OrderedSymbol& pair); - void logArchive(ObjectFile::Reader* reader); - void sortSections(); - void sortAtoms(); - void tweakLayout(); - void writeDotOutput(); - static bool minimizeStab(ObjectFile::Reader::Stab& stab); - static const char* truncateStabString(const char* str); - void collectDebugInfo(); - void writeOutput(); - ObjectFile::Atom* entryPoint(bool orInit, bool searchArchives=false); - ObjectFile::Atom* dyldClassicHelper(); - ObjectFile::Atom* dyldCompressedHelper(); - ObjectFile::Atom* dyldLazyLibraryHelper(); - const char* assureFullPath(const char* path); - void markLive(ObjectFile::Atom& atom, Linker::WhyLiveBackChain* previous); - void collectStabs(ObjectFile::Reader* reader, std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals); - void synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& allAtomsByReader); - void printStatistics(); - void printTime(const char* msg, uint64_t partTime, uint64_t totalTime); - char* commatize(uint64_t in, char* out); - void getVMInfo(vm_statistics_data_t& info); - cpu_type_t inferArchitecture(); - void checkDylibClientRestrictions(ObjectFile::Reader* reader); - void logDylib(ObjectFile::Reader* reader, bool indirect); - - void resolve(ObjectFile::Reference* reference); - void resolveFrom(ObjectFile::Reference* reference); - std::vector<class ObjectFile::Atom*>* addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy); - void addJustInTimeAtomsAndMarkLive(const char* name); - - ObjectFile::Reader* addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - ObjectFile::Reader* addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - ObjectFile::Reader* addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen); - - void logTraceInfo(const char* format, ...); - - - class SymbolTable - { - public: - typedef __gnu_cxx::hash_map<const char*, ObjectFile::Atom*, __gnu_cxx::hash<const char*>, CStringEquals> Mapper; - - SymbolTable(Linker&); - void require(const char* name); - bool add(ObjectFile::Atom& atom); - ObjectFile::Atom* find(const char* name); - void erase(const char* name); - unsigned int getRequireCount() { return fRequireCount; } - void getUndefinesNames(std::vector<const char*>& undefines); - void getTentativesNames(std::vector<const char*>& tents); - bool hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; } - bool hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; } - void setHasExternalWeakDefinitions(bool value) { fHasExternalWeakDefinitions = value; } - uint32_t dylibSymbolCount() { return fDylibSymbolCount; } - Mapper::iterator begin() { return fTable.begin(); } - Mapper::iterator end() { return fTable.end(); } - - private: - Linker& fOwner; - Mapper fTable; - unsigned int fRequireCount; - bool fHasExternalTentativeDefinitions; - bool fHasExternalWeakDefinitions; - uint32_t fDylibSymbolCount; - }; - - class AtomSorter - { - public: - AtomSorter(std::map<const ObjectFile::Atom*, uint32_t>* map, std::set<const ObjectFile::Atom*>& inits, - std::set<const ObjectFile::Atom*>& terms) : - fOverriddenOrdinalMap(map), fInitializerSet(inits), fTerminatorSet(terms) {} - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right); - private: - std::map<const ObjectFile::Atom*, uint32_t>* fOverriddenOrdinalMap; - std::set<const ObjectFile::Atom*>& fInitializerSet; - std::set<const ObjectFile::Atom*>& fTerminatorSet; - }; - - typedef std::map<const char*, uint32_t, CStringComparor> 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<const char*, std::vector<DTraceProbeInfo>, __gnu_cxx::hash<const char*>, CStringEquals> ProviderToProbes; - typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map<const char*, ObjectFile::Reader*, __gnu_cxx::hash<const char*>, CStringEquals> InstallNameToReader; - - struct IndirectLibrary { - const char* path; - uint64_t fileLen; - ObjectFile::Reader* reader; - std::set<ObjectFile::Reader*> parents; - ObjectFile::Reader* reExportedViaDirectLibrary; - }; - - ObjectFile::Reader* findDirectLibraryWhichReExports(struct IndirectLibrary& indirectLib); - - Options fOptions; - SymbolTable fGlobalSymbolTable; - uint32_t fNextInputOrdinal; - std::vector<class ObjectFile::Reader*> fInputFiles; - ExecutableFile::Writer* fOutputFile; - InstallNameToReader fDylibMap; - std::map<ObjectFile::Reader*,LibraryOptions> fDylibOptionsMap; - std::set<ObjectFile::Reader*> fDylibsProcessed; - ObjectFile::Reader* fBundleLoaderReader; - std::vector<class ObjectFile::Reader*> fReadersThatHaveSuppliedAtoms; - std::vector<class ObjectFile::Atom*> fAllAtoms; - std::set<class ObjectFile::Reader*> fArchiveReaders; - std::set<class ObjectFile::Reader*> fArchiveReadersLogged; - std::set<class ObjectFile::Atom*> fDeadAtoms; - std::set<ObjectFile::Atom*> fLiveAtoms; - std::set<ObjectFile::Atom*> fLiveRootAtoms; - std::set<const ObjectFile::Atom*> fInitializerAtoms; - std::set<const ObjectFile::Atom*> fTerminatorAtoms; - std::set<const ObjectFile::Atom*> fRegularDefAtomsThatOverrideADylibsWeakDef; - std::vector<class ObjectFile::Reader::Stab> fStabs; - std::vector<class ObjectFile::Atom*> fAtomsWithUnresolvedReferences; - std::set<class ObjectFile::Atom*> fAtomsOverriddenByLateLoads; - bool fInitialLoadsDone; - bool fCreateUUID; - bool fCanScatter; - SectionOrder fSectionOrder; - cpu_type_t fArchitecture; - const char* fArchitectureName; - bool fArchitectureInferred; - bool fDirectLibrariesComplete; - bool fBiggerThanTwoGigOutput; - uint64_t fOutputFileSize; - uint64_t fTotalZeroFillSize; - uint64_t fTotalSize; - uint64_t fStartTime; - uint64_t fStartCreateReadersTime; - uint64_t fStartCreateWriterTime; - uint64_t fStartBuildAtomsTime; - uint64_t fStartLoadAndResolveTime; - uint64_t fStartSortTime; - uint64_t fStartDebugTime; - uint64_t fStartWriteTime; - uint64_t fEndTime; - uint64_t fTotalObjectSize; - uint64_t fTotalArchiveSize; - uint32_t fTotalObjectLoaded; - uint32_t fTotalArchivesLoaded; - uint32_t fTotalDylibsLoaded; - vm_statistics_data_t fStartVMInfo; - ObjectFile::Reader::ObjcConstraint fCurrentObjCConstraint; - ObjectFile::Reader::CpuConstraint fCurrentCpuConstraint; - bool fObjcReplacmentClasses; - bool fAllDirectDylibsLoaded; -}; - - -Linker::Linker(int argc, const char* argv[]) - : fOptions(argc, argv), fGlobalSymbolTable(*this), fNextInputOrdinal(1), fOutputFile(NULL), fBundleLoaderReader(NULL), - fInitialLoadsDone(false), fCreateUUID(fOptions.outputKind() != Options::kObjectFile), fCanScatter(true), - fArchitecture(0), fArchitectureInferred(false), fDirectLibrariesComplete(false), fBiggerThanTwoGigOutput(false), - fOutputFileSize(0), fTotalZeroFillSize(0), fTotalSize(0), fTotalObjectSize(0), - fTotalArchiveSize(0), fTotalObjectLoaded(0), fTotalArchivesLoaded(0), fTotalDylibsLoaded(0), - fCurrentObjCConstraint(ObjectFile::Reader::kObjcNone), fCurrentCpuConstraint(ObjectFile::Reader::kCpuAny), - fObjcReplacmentClasses(false), fAllDirectDylibsLoaded(false) +const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect) { - fStartTime = mach_absolute_time(); - if ( fOptions.printStatistics() ) - getVMInfo(fStartVMInfo); - - fArchitecture = fOptions.architecture(); - if ( fArchitecture == 0 ) { - // -arch not specified, scan .o files to figure out what it should be - fArchitecture = inferArchitecture(); - fArchitectureInferred = true; - } - switch (fArchitecture) { - case CPU_TYPE_POWERPC: - fArchitectureName = "ppc"; - break; - case CPU_TYPE_POWERPC64: - fArchitectureName = "ppc64"; - break; - case CPU_TYPE_I386: - fArchitectureName = "i386"; + // merge sections in final linked image + switch ( sect.type() ) { + case ld::Section::typeLiteral4: + case ld::Section::typeLiteral8: + case ld::Section::typeLiteral16: + return _s_TEXT_const; + case ld::Section::typeUnclassified: + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) + return _s_DATA_data; + if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) + return _s_DATA_const; + } + else if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { + if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) + return _s_TEXT_const; + } break; - case CPU_TYPE_X86_64: - fArchitectureName = "x86_64"; + case ld::Section::typeCode: + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { + if ( strcmp(sect.sectionName(), "__textcoal_nt") == 0 ) + return _s_TEXT_text; + else if ( strcmp(sect.sectionName(), "__StaticInit") == 0 ) + return _s_TEXT_text; + } break; - case CPU_TYPE_ARM: - fArchitectureName = "arm"; - if ( fOptions.preferSubArchitecture() ) { - switch ( fOptions.subArchitecture() ) { - case CPU_SUBTYPE_ARM_V4T: - fArchitectureName = "armv4t"; - break; - case CPU_SUBTYPE_ARM_V5TEJ: - fArchitectureName = "armv5"; - break; - case CPU_SUBTYPE_ARM_V6: - fArchitectureName = "armv6"; - break; - case CPU_SUBTYPE_ARM_V7: - fArchitectureName = "armv7"; - break; - } + case ld::Section::typeNonLazyPointer: + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + if ( strcmp(sect.sectionName(), "__nl_symbol_ptr") == 0 ) + return _s_DATA_nl_symbol_ptr; + } + else if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) { + if ( strcmp(sect.sectionName(), "__pointers") == 0 ) + return _s_DATA_nl_symbol_ptr; } break; + case ld::Section::typeTentativeDefs: + return _s_DATA_common; + break; + // FIX ME: more default: - fArchitectureName = "unknown architecture"; break; } + return sect; } -const char* Linker::architectureName() -{ - return fArchitectureName; -} - -bool Linker::showArchitectureInErrors() +const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal) { - return fOptions.printArchPrefix(); + // in -r mode the only section that ever changes is __tenative -> __common with -d option + if ( (sect.type() == ld::Section::typeTentativeDefs) && makeTentativeDefsReal) + return _s_DATA_common; + return sect; } -bool Linker::isInferredArchitecture() +uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, bool objFile) { - return fArchitectureInferred; + if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) + return 0; + if ( strcmp(sect.segmentName(), "__HEADER") == 0 ) // only used with -preload + return 0; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) + return 1; + // in -r mode, want __DATA last so zerofill sections are at end + if ( strcmp(sect.segmentName(), "__DATA") == 0 ) + return (objFile ? 5 : 2); + if ( strcmp(sect.segmentName(), "__OBJC") == 0 ) + return 3; + if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) + return 4; + + // layout non-standard segments in order seen (+10 to shift beyond standard segments) + for (uint32_t i=0; i < _s_segmentsSeen.size(); ++i) { + if ( strcmp(_s_segmentsSeen[i], sect.segmentName()) == 0 ) + return i+10; + } + _s_segmentsSeen.push_back(sect.segmentName()); + return _s_segmentsSeen.size()-1+10; } -cpu_type_t Linker::inferArchitecture() +uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint32_t sectionsSeen) { - // scan all input files, looking for a thin .o file. - // the first one found is presumably the architecture to link - uint8_t buffer[sizeof(mach_header_64)]; - std::vector<Options::FileInfo>& files = fOptions.getInputFiles(); - for (std::vector<Options::FileInfo>::iterator it = files.begin(); it != files.end(); ++it) { - int fd = ::open(it->path, O_RDONLY, 0); - if ( fd != -1 ) { - ssize_t amount = read(fd, buffer, sizeof(buffer)); - ::close(fd); - if ( amount >= (ssize_t)sizeof(buffer) ) { - if ( mach_o::relocatable::Reader<ppc>::validFile(buffer) ) { - //warning("-arch not used, infering -arch ppc based on %s", it->path); - return CPU_TYPE_POWERPC; - } - else if ( mach_o::relocatable::Reader<ppc64>::validFile(buffer) ) { - //warning("-arch not used, infering -arch ppc64 based on %s", it->path); - return CPU_TYPE_POWERPC64; - } - else if ( mach_o::relocatable::Reader<x86>::validFile(buffer) ) { - //warning("-arch not used, infering -arch i386 based on %s", it->path); - return CPU_TYPE_I386; - } - else if ( mach_o::relocatable::Reader<x86_64>::validFile(buffer) ) { - //warning("-arch not used, infering -arch x86_64 based on %s", it->path); - return CPU_TYPE_X86_64; - } - else if ( mach_o::relocatable::Reader<arm>::validFile(buffer) ) { - //warning("-arch not used, infering -arch arm based on %s", it->path); - return CPU_TYPE_ARM; - } + if ( sect.type() == ld::Section::typeFirstSection ) + return 0; + if ( sect.type() == ld::Section::typeMachHeader ) + return 1; + if ( sect.type() == ld::Section::typeLastSection ) + return INT_MAX; + if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { + switch ( sect.type() ) { + case ld::Section::typeCode: + // <rdar://problem/8346444> make __text always be first "code" section + if ( strcmp(sect.sectionName(), "__text") == 0 ) + return 10; + else + return 11; + case ld::Section::typeStub: + return 12; + case ld::Section::typeStubHelper: + return 13; + case ld::Section::typeLSDA: + return INT_MAX-3; + case ld::Section::typeUnwindInfo: + return INT_MAX-2; + case ld::Section::typeCFI: + return INT_MAX-1; + case ld::Section::typeStubClose: + return INT_MAX; + default: + return sectionsSeen+20; + } + } + else if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + switch ( sect.type() ) { + case ld::Section::typeLazyPointerClose: + return 8; + case ld::Section::typeDyldInfo: + return 9; + case ld::Section::typeNonLazyPointer: + return 10; + case ld::Section::typeLazyPointer: + return 11; + case ld::Section::typeInitializerPointers: + return 12; + case ld::Section::typeTerminatorPointers: + return 13; + case ld::Section::typeTLVInitialValues: + return INT_MAX-4; // need TLV zero-fill to follow TLV init values + case ld::Section::typeTLVZeroFill: + return INT_MAX-3; + case ld::Section::typeZeroFill: + // make sure __huge is always last zerofill section + if ( strcmp(sect.sectionName(), "__huge") == 0 ) + return INT_MAX-1; + else + return INT_MAX-2; + default: + // <rdar://problem/7435296> Reorder sections to reduce page faults in object files + if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) + return 20; + else if ( strcmp(sect.sectionName(), "__objc_nlclslist") == 0 ) + return 21; + else if ( strcmp(sect.sectionName(), "__objc_catlist") == 0 ) + return 22; + else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 ) + return 23; + else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 ) + return 24; + else if ( strcmp(sect.sectionName(), "__objc_const") == 0 ) + return 25; + else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 ) + return 26; + else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 ) + return 27; + else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 ) + return 28; + else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 ) + return 29; + else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 ) + return 30; + else if ( strcmp(sect.sectionName(), "__objc_data") == 0 ) + return 31; + else + return sectionsSeen+40; + } + } + // make sure zerofill in any other section is at end of segment + if ( sect.type() == ld::Section::typeZeroFill ) + return INT_MAX-1; + return sectionsSeen+20; +} + +#ifndef NDEBUG +static void validateFixups(const ld::Atom& atom) +{ + //fprintf(stderr, "validateFixups %s\n", atom.name()); + bool lastWasClusterEnd = true; + ld::Fixup::Cluster lastClusterSize = ld::Fixup::k1of1; + uint32_t curClusterOffsetInAtom = 0; + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + //fprintf(stderr, " fixup offset=%d, cluster=%d\n", fit->offsetInAtom, fit->clusterSize); + assert((fit->offsetInAtom < atom.size()) || (fit->offsetInAtom == 0)); + if ( fit->firstInCluster() ) { + assert(lastWasClusterEnd); + curClusterOffsetInAtom = fit->offsetInAtom; + lastWasClusterEnd = (fit->clusterSize == ld::Fixup::k1of1); + } + else { + assert(!lastWasClusterEnd); + assert(fit->offsetInAtom == curClusterOffsetInAtom); + switch ((ld::Fixup::Cluster)fit->clusterSize) { + case ld::Fixup::k1of1: + case ld::Fixup::k1of2: + case ld::Fixup::k1of3: + case ld::Fixup::k1of4: + case ld::Fixup::k1of5: + lastWasClusterEnd = false; + break; + case ld::Fixup::k2of2: + assert(lastClusterSize = ld::Fixup::k1of2); + lastWasClusterEnd = true; + break; + case ld::Fixup::k2of3: + assert(lastClusterSize = ld::Fixup::k1of3); + lastWasClusterEnd = false; + break; + case ld::Fixup::k2of4: + assert(lastClusterSize = ld::Fixup::k1of4); + lastWasClusterEnd = false; + break; + case ld::Fixup::k2of5: + assert(lastClusterSize = ld::Fixup::k1of5); + lastWasClusterEnd = false; + break; + case ld::Fixup::k3of3: + assert(lastClusterSize = ld::Fixup::k2of3); + lastWasClusterEnd = true; + break; + case ld::Fixup::k3of4: + assert(lastClusterSize = ld::Fixup::k2of4); + lastWasClusterEnd = false; + break; + case ld::Fixup::k3of5: + assert(lastClusterSize = ld::Fixup::k2of5); + lastWasClusterEnd = false; + break; + case ld::Fixup::k4of4: + assert(lastClusterSize = ld::Fixup::k3of4); + lastWasClusterEnd = true; + break; + case ld::Fixup::k4of5: + assert(lastClusterSize = ld::Fixup::k3of5); + lastWasClusterEnd = false; + break; + case ld::Fixup::k5of5: + assert(lastClusterSize = ld::Fixup::k4of5); + lastWasClusterEnd = true; + break; } } + lastClusterSize = fit->clusterSize; + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + assert(fit->u.target != NULL); + } } - - // no thin .o files found, so default to same architecture this was built as - warning("-arch not specified"); -#if __ppc__ - return CPU_TYPE_POWERPC; -#elif __i386__ - return CPU_TYPE_I386; -#elif __ppc64__ - return CPU_TYPE_POWERPC64; -#elif __x86_64__ - return CPU_TYPE_X86_64; -#elif __arm__ - return CPU_TYPE_ARM; -#else - #error unknown default architecture -#endif -} - - -void Linker::addInputFile(ObjectFile::Reader* reader, const Options::FileInfo& info) -{ - fInputFiles.push_back(reader); - fDylibOptionsMap[reader] = info.options; -} - -void Linker::setOutputFile(ExecutableFile::Writer* writer) -{ - fOutputFile = writer; -} - -class InSet -{ -public: - InSet(std::set<ObjectFile::Atom*>& deadAtoms) : fDeadAtoms(deadAtoms) {} - - bool operator()(ObjectFile::Atom*& atom) const { - return ( fDeadAtoms.count(atom) != 0 ); - } - -private: - std::set<ObjectFile::Atom*>& fDeadAtoms; -}; - -void Linker::loadAndResolve() -{ - fStartLoadAndResolveTime = mach_absolute_time(); - if ( fOptions.deadStrip() == Options::kDeadStripOff ) { - // without dead-code-stripping: - // find atoms to resolve all undefines - this->loadUndefines(); - // verify nothing is missing - this->checkUndefines(); - // once all undefines fulfill, then bind all references - this->resolveReferences(); - // remove atoms weak atoms that have been overridden - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - } - else { - // with dead code stripping: - // start binding references from roots, - this->deadStripResolve(); - // verify nothing is missing - this->checkUndefines(); + switch (lastClusterSize) { + case ld::Fixup::k1of1: + case ld::Fixup::k2of2: + case ld::Fixup::k3of3: + case ld::Fixup::k4of4: + case ld::Fixup::k5of5: + break; + default: + assert(0 && "last fixup was not end of cluster"); + break; } } +#endif -void Linker::addSynthesizedAtoms() -{ - // give write a chance to synthesize stub, GOT, and lazy pointer atoms - std::vector<class ObjectFile::Atom*> newAtoms; - fOutputFile->addSynthesizedAtoms(fAllAtoms, this->dyldClassicHelper(), - this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(), - fBiggerThanTwoGigOutput, - fGlobalSymbolTable.dylibSymbolCount(), - newAtoms); - - // add all newly created atoms to fAllAtoms and update symbol table - this->addAtoms(newAtoms); -} - -void Linker::optimize() -{ - // give each reader a chance to do any optimizations - bool didSomething = false; - std::vector<class ObjectFile::Atom*> newAtoms; - std::vector<const char *> additionalUndefines; - std::vector<class ObjectFile::Atom*> newlyDeadAtoms; - for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - didSomething |= (*it)->optimize(fAllAtoms, newAtoms, additionalUndefines, fDeadAtoms, newlyDeadAtoms, fNextInputOrdinal, - fOutputFile, entryPoint(true), fOptions.llvmOptions(), - fOptions.allGlobalsAreDeadStripRoots(), (int)fOptions.outputKind(), fOptions.verbose(), - fOptions.saveTempFiles(), fOptions.getOutputFilePath(), fOptions.positionIndependentExecutable(), - fOptions.allowTextRelocs()); - } - - // only do next steps if some optimization was actually done - if ( didSomething ) { - - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - for(std::vector<class ObjectFile::Atom*>::iterator itr = newAtoms.begin(); itr != newAtoms.end(); ++itr) { - ObjectFile::Atom* atom = *itr; - const char* name = atom->getName(); - if ( name != NULL ) { - ObjectFile::Atom* existingAtom = fGlobalSymbolTable.find(name); - if ( (existingAtom != NULL) && fLiveAtoms.count(existingAtom) == 0 ) { - // While dead code stripping, the atoms were not removed from fGlobalSymbolTable - // for performance reasons. Normally, libLTO will never recreate an atom - // that was previously dead stripped away, but if it does remove - // the remnents of the previous so the new one can be added - fGlobalSymbolTable.erase(name); +ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) +{ + ld::Internal::FinalSection* fs = this->getFinalSection(atom.section()); + + // <rdar://problem/8612550> When order file used on data, turn ordered zero fill symbols into zero data + switch ( atom.section().type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTentativeDefs: + if ( (_options.outputKind() == Options::kDyld) && (atom.symbolTableInclusion() == ld::Atom::symbolTableIn) + && (atom.size() <= 512) && (_options.orderedSymbolsCount() != 0) ) { + for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) { + if ( (it->objectFileName == NULL) && (strcmp(it->symbolName, atom.name()) == 0) ) { + // found in order file, move to __data section + fs = getFinalSection(InternalState::FinalSection::_s_DATA_data);\ + //fprintf(stderr, "moved %s to __data section\n", atom.name()); + break; } } } - } - - // add all newly created atoms to fAllAtoms and update symbol table - this->addAtoms(newAtoms); - - // add dead atoms to dead list and remove from fAllAtoms - for(std::vector<class ObjectFile::Atom*>::iterator itr = newlyDeadAtoms.begin(); itr != newlyDeadAtoms.end(); ++itr) - markDead(*itr); - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - - // Make sure all atoms have a section. Atoms that were not originally in a mach-o file could - // not have their section set until now. - for(std::vector<class ObjectFile::Atom*>::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - if ( atom->getSection() == NULL ) - atom->setSection(Section::find(atom->getSectionName(), atom->getSegment().getName(), atom->isZeroFill(), true)); - } - - // resolve new undefines - for(std::vector<const char*>::iterator riter = additionalUndefines.begin(); riter != additionalUndefines.end(); ++riter) { - const char *targetName = *riter; - //fprintf(stderr, "LTO additional undefine: %s\n", targetName); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL) { - // mark that this symbol is needed - fGlobalSymbolTable.require(targetName); - // try to find it in some library - this->addJustInTimeAtoms(targetName, true, true, true); - } - } - - if ( fOptions.deadStrip() != Options::kDeadStripOff ) { - // LTO may optimize away some atoms, so dead stripping must be redone - fLiveAtoms.clear(); - this->deadStripResolve(); - this->checkUndefines(); - } - else { - // LTO may require new library symbols to be loaded, so redo - this->checkUndefines(); - this->resolveReferences(); - } + break; + default: + break; } + + //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs); +#ifndef NDEBUG + validateFixups(atom); +#endif + fs->atoms.push_back(&atom); + return fs; } +ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& inputSection) +{ + const ld::Section* baseForFinalSection = &inputSection; + + // see if input section already has a FinalSection + SectionInToOut::iterator pos = _sectionInToFinalMap.find(&inputSection); + if ( pos != _sectionInToFinalMap.end() ) { + return pos->second; + } -void Linker::adjustScope() -{ - // if -exported_symbols_list is used, demoted to hidden, symbols that are not in it - if ( fOptions.hasExportRestrictList() ) { - // The use of an -export file means the previous computation of fHasExternalWeakDefinitions could change - fGlobalSymbolTable.setHasExternalWeakDefinitions(false); - for(std::vector<class ObjectFile::Atom*>::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - ObjectFile::Atom::Scope scope = atom->getScope(); - const char* name = atom->getName(); - if ( name != NULL ) { - if ( scope == ObjectFile::Atom::scopeGlobal ) { - // check for globals that are downgraded to hidden - if ( !fOptions.shouldExport(name) ) { - atom->setScope(ObjectFile::Atom::scopeLinkageUnit); - //fprintf(stderr, "demote %s to hidden\n", name); - } - else if ( atom->getDefinitionKind() == ObjectFile::Atom::kWeakDefinition ) { - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn ) { - // we do have an exported weak symbol, turn WEAK_DEFINES back on - fGlobalSymbolTable.setHasExternalWeakDefinitions(true); - } - } + // otherwise, create a new final section + bool objFile = false; + switch ( _options.outputKind() ) { + case Options::kStaticExecutable: + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + case Options::kKextBundle: + case Options::kPreload: + { + // coalesce some sections + const ld::Section& outSect = FinalSection::outputSection(inputSection); + pos = _sectionInToFinalMap.find(&outSect); + if ( pos != _sectionInToFinalMap.end() ) { + _sectionInToFinalMap[&inputSection] = pos->second; + //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second); + return pos->second; } - else if ( scope == ObjectFile::Atom::scopeLinkageUnit ) { - // check for hiddens that were requested to be exported - if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { - warning("cannot export hidden symbol %s from %s", name, atom->getFile()->getPath()); - } + else if ( outSect != inputSection ) { + // new output section created, but not in map + baseForFinalSection = &outSect; } } - } - } - - // linking is done, so demote hidden symbols to static - if ( (fOptions.outputKind() == Options::kObjectFile) && fOptions.keepPrivateExterns() ) { - // ld -r -keep_private_externs does not move hidden symbols to static - } - else { - for(std::vector<class ObjectFile::Atom*>::iterator itr = fAllAtoms.begin(); itr != fAllAtoms.end(); ++itr) { - ObjectFile::Atom *atom = *itr; - // <rdar://problem/4637139> hidden common symbols cannot be demoted to static - if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && (atom->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition) ) { - atom->setScope(ObjectFile::Atom::scopeTranslationUnit); - //fprintf(stderr, "demote %s to static\n", atom->getDisplayName()); + break; + case Options::kObjectFile: + baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options.makeTentativeDefinitionsReal()); + pos = _sectionInToFinalMap.find(baseForFinalSection); + if ( pos != _sectionInToFinalMap.end() ) { + _sectionInToFinalMap[&inputSection] = pos->second; + //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second); + return pos->second; } - } + objFile = true; + break; } -} - -void Linker::link() -{ - this->buildAtomList(); - this->loadAndResolve(); - this->optimize(); - this->adjustScope(); - this->checkObjC(); - this->processDTrace(); - this->tweakLayout(); - this->addSynthesizedAtoms(); - this->sortSections(); - this->sortAtoms(); - this->writeDotOutput(); - this->collectDebugInfo(); - this->writeOutput(); - this->printStatistics(); - if ( fOptions.pauseAtEnd() ) - sleep(10); + InternalState::FinalSection* result = new InternalState::FinalSection(*baseForFinalSection, + _sectionInToFinalMap.size(), objFile); + _sectionInToFinalMap[baseForFinalSection] = result; + //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", baseForFinalSection, result); + sections.push_back(result); + return result; } -void Linker::printTime(const char* msg, uint64_t partTime, uint64_t totalTime) + +int InternalState::FinalSection::sectionComparer(const void* l, const void* r) { - static uint64_t sUnitsPerSecond = 0; - if ( sUnitsPerSecond == 0 ) { - struct mach_timebase_info timeBaseInfo; - if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { - sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; - //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); - } - } - if ( partTime < sUnitsPerSecond ) { - uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; - uint32_t milliSeconds = milliSecondsTimeTen/10; - uint32_t percentTimesTen = (partTime*1000)/totalTime; - uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); - } - else { - uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; - uint32_t seconds = secondsTimeTen/10; - uint32_t percentTimesTen = (partTime*1000)/totalTime; - uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); - } + const FinalSection* left = *(FinalSection**)l; + const FinalSection* right = *(FinalSection**)r; + if ( left->_segmentOrder != right->_segmentOrder ) + return (left->_segmentOrder - right->_segmentOrder); + return (left->_sectionOrder - right->_sectionOrder); } -char* Linker::commatize(uint64_t in, char* out) +void InternalState::sortSections() { - char* result = out; - char rawNum[30]; - sprintf(rawNum, "%llu", in); - const int rawNumLen = strlen(rawNum); - for(int i=0; i < rawNumLen-1; ++i) { - *out++ = rawNum[i]; - if ( ((rawNumLen-i) % 3) == 1 ) - *out++ = ','; - } - *out++ = rawNum[rawNumLen-1]; - *out = '\0'; - return result; + //fprintf(stderr, "UNSORTED final sections:\n"); + //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { + // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); + //} + qsort(§ions[0], sections.size(), sizeof(FinalSection*), &InternalState::FinalSection::sectionComparer); + //fprintf(stderr, "SORTED final sections:\n"); + //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { + // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); + //} + assert((sections[0]->type() == ld::Section::typeMachHeader) + || ((sections[0]->type() == ld::Section::typeFirstSection) && (sections[1]->type() == ld::Section::typeMachHeader)) + || ((sections[0]->type() == ld::Section::typePageZero) && (sections[1]->type() == ld::Section::typeMachHeader)) + || ((sections[0]->type() == ld::Section::typePageZero) && (sections[1]->type() == ld::Section::typeFirstSection) && (sections[2]->type() == ld::Section::typeMachHeader)) ); + } -void Linker::getVMInfo(vm_statistics_data_t& info) +static void getVMInfo(vm_statistics_data_t& info) { mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO, @@ -901,3331 +553,113 @@ void Linker::getVMInfo(vm_statistics_data_t& info) } } -void Linker::printStatistics() +int main(int argc, const char* argv[]) { - fEndTime = mach_absolute_time(); - if ( fOptions.printStatistics() ) { - vm_statistics_data_t endVMInfo; - getVMInfo(endVMInfo); - - uint64_t totalTime = fEndTime - fStartTime; - printTime("ld total time", totalTime, totalTime); - printTime(" option parsing time", fStartCreateReadersTime - fStartTime, totalTime); - printTime(" object file processing",fStartCreateWriterTime - fStartCreateReadersTime, totalTime); - printTime(" output file setup", fStartBuildAtomsTime - fStartCreateWriterTime, totalTime); - printTime(" build atom list", fStartLoadAndResolveTime - fStartBuildAtomsTime, totalTime); - printTime(" resolve references", fStartSortTime - fStartLoadAndResolveTime, totalTime); - printTime(" sort output", fStartDebugTime - fStartSortTime, totalTime); - printTime(" process debug info", fStartWriteTime - fStartDebugTime, totalTime); - printTime(" write output", fEndTime - fStartWriteTime, totalTime); - fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", endVMInfo.pageins-fStartVMInfo.pageins, - endVMInfo.pageouts-fStartVMInfo.pageouts, endVMInfo.faults-fStartVMInfo.faults); - char temp[40]; - fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", fTotalObjectLoaded, commatize(fTotalObjectSize, temp)); - fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", fTotalArchivesLoaded, commatize(fTotalArchiveSize, temp)); - fprintf(stderr, "processed %3u dylib files\n", fTotalDylibsLoaded); - fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(fOutputFileSize, temp)); +#if DEBUG + usleep(1000000); +#endif + const char* archName = NULL; + bool showArch = false; + bool archInferred = false; + try { + vm_statistics_data_t vmStart; + vm_statistics_data_t vmEnd; + getVMInfo(vmStart); + + // create object to track command line arguments + Options options(argc, argv); + + // gather stats + if ( options.printStatistics() ) + getVMInfo(vmStart); + + // update strings for error messages + showArch = options.printArchPrefix(); + archName = options.architectureName(); + archInferred = (options.architecture() == 0); + + // open and parse input files + ld::tool::InputFiles inputFiles(options, &archName); + + // load and resolve all references + InternalState state(options); + ld::tool::Resolver resolver(options, inputFiles, state); + resolver.resolve(); + + // add dylibs used + inputFiles.dylibs(state); + + // do initial section sorting so passes have rough idea of the layout + state.sortSections(); + + // run passes + ld::passes::objc::doPass(options, state); + ld::passes::stubs::doPass(options, state); + ld::passes::huge::doPass(options, state); + ld::passes::got::doPass(options, state); + ld::passes::tlvp::doPass(options, state); + ld::passes::dylibs::doPass(options, state); // must be after stubs and GOT passes + ld::passes::order_file::doPass(options, state); + ld::passes::branch_shim::doPass(options, state); // must be after stubs + ld::passes::branch_island::doPass(options, state); // must be after stubs and order_file pass + ld::passes::dtrace::doPass(options, state); + ld::passes::compact_unwind::doPass(options, state); // must be after order-file pass + + // sort final sections + state.sortSections(); + + // write output file + ld::tool::OutputFile out(options); + out.write(state); + + // print statistics + //mach_o::relocatable::printCounts(); + if ( options.printStatistics() ) { + getVMInfo(vmEnd); + fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", vmEnd.pageins-vmStart.pageins, + vmEnd.pageouts-vmStart.pageouts, vmEnd.faults-vmStart.faults); + + } + } + catch (const char* msg) { + if ( archInferred ) + fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); + else if ( showArch ) + fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); + else + fprintf(stderr, "ld: %s\n", msg); + return 1; } + + return 0; } -inline void Linker::addAtom(ObjectFile::Atom& atom) + +#ifndef NDEBUG +// implement assert() function to print out a backtrace before aborting +void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) { - // add to list of all atoms - fAllAtoms.push_back(&atom); + fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); - if ( fOptions.deadStrip() == Options::kDeadStripOff ) { - // not dead-stripping code, so add atom's references's names to symbol table as to-be-resolved-later - std::vector<class ObjectFile::Reference*>& references = atom.getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) - fGlobalSymbolTable.require(reference->getTargetName()); - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) - fGlobalSymbolTable.require(reference->getFromTargetName()); - } - // update total size info (except for __ZEROPAGE atom) - if ( atom.getSegment().isContentReadable() ) { - fTotalSize += atom.getSize(); - if ( atom.isZeroFill() ) - fTotalZeroFillSize += atom.getSize(); + void* callStack[128]; + int depth = ::backtrace(callStack, 128); + char* buffer = (char*)malloc(1024); + for(int i=0; i < depth-1; ++i) { + Dl_info info; + dladdr(callStack[i], &info); + const char* symboName = info.dli_sname; + if ( (symboName != NULL) && (strncmp(symboName, "_Z", 2) == 0) ) { + size_t bufLen = 1024; + int result; + char* unmangled = abi::__cxa_demangle(symboName, buffer, &bufLen, &result); + if ( unmangled != NULL ) + symboName = unmangled; } + long offset = (uintptr_t)callStack[i] - (uintptr_t)info.dli_saddr; + fprintf(stderr, "%d %p %s + %ld\n", i, callStack[i], symboName, offset); } - else { - if ( atom.dontDeadStrip() ) - fLiveRootAtoms.insert(&atom); - } + exit(1); +} +#endif - // if in global namespace, add atom itself to symbol table - ObjectFile::Atom::Scope scope = atom.getScope(); - const char* name = atom.getName(); - if ( (scope != ObjectFile::Atom::scopeTranslationUnit) && (name != NULL) ) { - // add to symbol table - fGlobalSymbolTable.add(atom); - } - // record section orders so output file can have same order - if (atom.getSectionName()) { - bool untrusted = false; - switch ( atom.getContentType() ) { - case ObjectFile::Atom::kSectionStart: - case ObjectFile::Atom::kSectionEnd: - untrusted = true; - default: - break; - } - atom.setSection(Section::find(atom.getSectionName(), atom.getSegment().getName(), atom.isZeroFill(), untrusted)); - } -} - - -void Linker::markDead(ObjectFile::Atom* atom) -{ - //fprintf(stderr, "markDead(%p) %s from %s\n", atom, atom->getDisplayName(), atom->getFile()->getPath()); - fDeadAtoms.insert(atom); - - // <rdar://problem/6578360> -dead_strip inhibits weak coalescing in no_dead_strip section - if ( fLiveRootAtoms.count(atom) != 0 ) { - fLiveRootAtoms.erase(atom); - } - - // - // The kGroupSubordinate reference kind is used to model group comdat. - // The "signature" atom in the group has a kGroupSubordinate reference to - // all other members of the group. So, if the signature atom is - // coalesced away, all other atoms in the group should also be removed. - // - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* ref = *rit; - if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) { // FIX FIX - ObjectFile::Atom* targetAtom = &(ref->getTarget()); - //fprintf(stderr, " markDead(%p) subordinate %s\n", targetAtom, targetAtom->getDisplayName()); - if ( targetAtom == NULL ) { - warning("%s has a group reference to %s but is not bound", atom->getDisplayName(), ref->getTargetName()); - } - else { - if ( targetAtom->getScope() != ObjectFile::Atom::scopeTranslationUnit ) { - // ok for .eh symbols to be not static in -r mode - if ( (fOptions.outputKind() != Options::kObjectFile) || (strcmp(targetAtom->getSectionName(), "__eh_frame") != 0) ) - warning("%s is in a comdat group but its scope is not static", targetAtom->getDisplayName()); - } - this->markDead(targetAtom); - } - } - } -} - -void Linker::updateConstraints(ObjectFile::Reader* reader) -{ - // check objc objects were compiled compatibly - ObjectFile::Reader::ObjcConstraint objcAddition = reader->getObjCConstraint(); - if ( reader->getInstallPath() == NULL ) { - // adding a .o file - switch ( objcAddition ) { - case ObjectFile::Reader::kObjcNone: - break; - case ObjectFile::Reader::kObjcRetainRelease: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcGC ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); - fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainRelease; - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcNone ) - fCurrentObjCConstraint = ObjectFile::Reader::kObjcRetainReleaseOrGC; - break; - case ObjectFile::Reader::kObjcGC: - if ( fCurrentObjCConstraint == ObjectFile::Reader::kObjcRetainRelease ) - throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", reader->getPath()); - fCurrentObjCConstraint = ObjectFile::Reader::kObjcGC; - break; - } - } - if ( reader->objcReplacementClasses() ) - fObjcReplacmentClasses = true; - - // check cpu sub-types for stricter sub-type - fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)reader->updateCpuConstraint(fCurrentCpuConstraint); -} - -inline void Linker::addAtoms(std::vector<class ObjectFile::Atom*>& atoms) -{ - bool scanAll = fOptions.readerOptions().fFullyLoadArchives || fOptions.readerOptions().fLoadAllObjcObjectsFromArchives; - bool first = true; - for (std::vector<ObjectFile::Atom*>::iterator it=atoms.begin(); it != atoms.end(); it++) { - // usually we only need to get the first atom's reader, but - // with -all_load all atoms from all .o files come come back together - // so we need to scan all atoms - if ( first || scanAll ) { - // update fReadersThatHaveSuppliedAtoms - ObjectFile::Reader* reader = (*it)->getFile(); - if ( std::find(fReadersThatHaveSuppliedAtoms.begin(), fReadersThatHaveSuppliedAtoms.end(), reader) - == fReadersThatHaveSuppliedAtoms.end() ) { - fReadersThatHaveSuppliedAtoms.push_back(reader); - updateConstraints(reader); - } - } - this->addAtom(**it); - first = false; - } -} - -void Linker::logArchive(ObjectFile::Reader* reader) -{ - if ( (fArchiveReaders.count(reader) != 0) && (fArchiveReadersLogged.count(reader) == 0) ) { - fArchiveReadersLogged.insert(reader); - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - logTraceInfo("[Logging for XBS] Used static archive: %s\n", fullPath); - } -} - - -void Linker::buildAtomList() -{ - fStartBuildAtomsTime = mach_absolute_time(); - // add initial undefines from -u option - std::vector<const char*>& initialUndefines = fOptions.initialUndefines(); - for (std::vector<const char*>::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) { - fGlobalSymbolTable.require(*it); - } - - // writer can contribute atoms - this->addAtoms(fOutputFile->getAtoms()); - - // each reader contributes atoms - for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - std::vector<class ObjectFile::Atom*>& atoms = reader->getAtoms(); - this->addAtoms(atoms); - if ( fOptions.readerOptions().fTraceArchives && (atoms.size() != 0) ) - logArchive(reader); - } - - // extra command line section always at end - std::vector<Options::ExtraSection>& extraSections = fOptions.extraSections(); - for( std::vector<Options::ExtraSection>::iterator it=extraSections.begin(); it != extraSections.end(); ++it) { - this->addAtoms((new opaque_section::Reader(it->segmentName, it->sectionName, it->path, it->data, it->dataLen, fNextInputOrdinal))->getAtoms()); - fNextInputOrdinal += it->dataLen; - } - - // done with all .o files on command line - // everything loaded from now on is a just-in-time atom - fInitialLoadsDone = true; -} - -static const char* pathLeafName(const char* path) -{ - const char* shortPath = strrchr(path, '/'); - if ( shortPath == NULL ) - return path; - else - return &shortPath[1]; -} - - -void Linker::loadUndefines() -{ - // keep looping until no more undefines were added in last loop - unsigned int undefineCount = 0xFFFFFFFF; - while ( undefineCount != fGlobalSymbolTable.getRequireCount() ) { - undefineCount = fGlobalSymbolTable.getRequireCount(); - std::vector<const char*> undefineNames; - fGlobalSymbolTable.getUndefinesNames(undefineNames); - for(std::vector<const char*>::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - // load for previous undefine may also have loaded this undefine, so check again - if ( fGlobalSymbolTable.find(*it) == NULL ) { - std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(*it, true, true, true); - if ( atoms != NULL ) - delete atoms; - } - } - // <rdar://problem/5894163> need to search archives for overrides of common symbols - if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { - bool searchDylibs = (fOptions.commonsMode() == Options::kCommonsOverriddenByDylibs); - std::vector<const char*> tentativeDefinitionNames; - fGlobalSymbolTable.getTentativesNames(tentativeDefinitionNames); - for(std::vector<const char*>::iterator it = tentativeDefinitionNames.begin(); it != tentativeDefinitionNames.end(); ++it) { - // load for previous tentative may also have overridden this tentative, so check again - ObjectFile::Atom* tent = fGlobalSymbolTable.find(*it); - if ( (tent != NULL) && (tent->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { - std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(*it, searchDylibs, true, false); - if ( atoms != NULL ) - delete atoms; - } - } - } - } -} - -// temp hack for rdar://problem/4718189 map ObjC class names to new runtime names -class ExportedObjcClass -{ -public: - ExportedObjcClass(Options& opt) : fOptions(opt) {} - - bool operator()(const char* name) const { - if ( fOptions.shouldExport(name) ) { - if ( strncmp(name, ".objc_class_name_", 17) == 0 ) - return true; - if ( strncmp(name, "_OBJC_CLASS_$_", 14) == 0 ) - return true; - if ( strncmp(name, "_OBJC_METACLASS_$_", 18) == 0 ) - return true; - } - //fprintf(stderr, "%s is not exported\n", name); - return false; - } -private: - Options& fOptions; -}; - - -void Linker::checkUndefines() -{ - // error out on any remaining undefines - bool doPrint = true; - bool doError = true; - switch ( fOptions.undefinedTreatment() ) { - case Options::kUndefinedError: - break; - case Options::kUndefinedDynamicLookup: - doError = false; - break; - case Options::kUndefinedWarning: - doError = false; - break; - case Options::kUndefinedSuppress: - doError = false; - doPrint = false; - break; - } - std::vector<const char*> unresolvableUndefines; - fGlobalSymbolTable.getUndefinesNames(unresolvableUndefines); - - // temp hack for rdar://problem/4718189 map ObjC class names to new runtime names - // ignore unresolved references to Objc class names that are listed in -exported_symbols_list - if ( fOptions.hasExportRestrictList() ) - unresolvableUndefines.erase(std::remove_if(unresolvableUndefines.begin(), unresolvableUndefines.end(), ExportedObjcClass(fOptions)), unresolvableUndefines.end()); - - const int unresolvableCount = unresolvableUndefines.size(); - int unresolvableExportsCount = 0; - if ( unresolvableCount != 0 ) { - if ( doPrint ) { - if ( fOptions.printArchPrefix() ) - fprintf(stderr, "Undefined symbols for architecture %s:\n", fArchitectureName); - else - fprintf(stderr, "Undefined symbols:\n"); - for (int i=0; i < unresolvableCount; ++i) { - const char* name = unresolvableUndefines[i]; - fprintf(stderr, " \"%s\", referenced from:\n", name); - // scan all atoms for references - bool foundAtomReference = false; - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - if ( strcmp(reference->getTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } - } - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - if ( strcmp(reference->getFromTargetName(), name) == 0 ) { - fprintf(stderr, " %s in %s\n", atom->getDisplayName(), pathLeafName(atom->getFile()->getPath())); - foundAtomReference = true; - } - } - } - } - // scan command line options - if ( !foundAtomReference ) { - // might be from -init command line option - if ( (fOptions.initFunctionName() != NULL) && (strcmp(name, fOptions.initFunctionName()) == 0) ) { - fprintf(stderr, " -init command line option\n"); - } - // or might be from exported symbol option - else if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { - fprintf(stderr, " -exported_symbol[s_list] command line option\n"); - } - else { - bool isInitialUndefine = false; - std::vector<const char*>& clundefs = fOptions.initialUndefines(); - for (std::vector<const char*>::iterator uit = clundefs.begin(); uit != clundefs.end(); ++uit) { - if ( strcmp(*uit, name) == 0 ) { - isInitialUndefine = true; - break; - } - } - if ( isInitialUndefine ) - fprintf(stderr, " -u command line option\n"); - } - ++unresolvableExportsCount; - } - // be helpful and check for typos - bool printedStart = false; - for (SymbolTable::Mapper::iterator sit=fGlobalSymbolTable.begin(); sit != fGlobalSymbolTable.end(); ++sit) { - if ( (sit->second != NULL) && (strstr(sit->first, name) != NULL) ) { - if ( ! printedStart ) { - fprintf(stderr, " (maybe you meant: %s", sit->first); - printedStart = true; - } - else { - fprintf(stderr, ", %s ", sit->first); - } - } - } - if ( printedStart ) - fprintf(stderr, ")\n"); - } - } - if ( doError ) - throw "symbol(s) not found"; - } - - // for each tentative definition in symbol table look for dylib that exports same symbol name - if ( fGlobalSymbolTable.hasExternalTentativeDefinitions() ) { - for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { - ObjectFile::Atom* atom = it->second; - if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kTentativeDefinition) - && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { - // look for dylibs that export same name as used by global tentative definition - addJustInTimeAtoms(atom->getName(), true, false, false); - } - } - } - - - // record any overrides of weak symbols any linked dylib - for (SymbolTable::Mapper::iterator it=fGlobalSymbolTable.begin(); it != fGlobalSymbolTable.end(); ++it) { - ObjectFile::Atom* atom = it->second; - if ( (atom != NULL) && (atom->getDefinitionKind()==ObjectFile::Atom::kRegularDefinition) - && (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) { - const char* name = atom->getName(); - //fprintf(stderr, "looking for dylibs with a weak %s\n", name); - // look for dylibs with weak exports of the same name - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - ObjectFile::Reader* reader = it->second; - if ( reader->hasWeakExternals() ) { - std::vector<class ObjectFile::Atom*>* dylibAtoms = reader->getJustInTimeAtomsFor(name); - if ( dylibAtoms != NULL ) { - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - // if this is a weak definition in a dylib - if ( (dylibAtoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fRegularDefAtomsThatOverrideADylibsWeakDef.insert(atom); - } - } - } - } - } - } - -} - - - -std::vector<class ObjectFile::Atom*>* Linker::addJustInTimeAtoms(const char* name, bool searchDylibs, bool searchArchives, bool okToMakeProxy) -{ - //fprintf(stderr, "addJustInTimeAtoms(%s, searchDylibs=%d, searchArchives=%d)\n", name, searchDylibs, searchArchives ); - // when creating final linked image, writer gets first chance - if ( fOptions.outputKind() != Options::kObjectFile ) { - std::vector<class ObjectFile::Atom*>* atoms = fOutputFile->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, fOutputFile->getPath() ); - return atoms; // found a definition, no need to search anymore - } - } - - // give readers a chance - for (std::vector<class ObjectFile::Reader*>::iterator it=fInputFiles.begin(); it != fInputFiles.end(); it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - // if this reader is a static archive that has the symbol we need, pull in all atoms in that module - // if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us. - //fprintf(stderr, "addJustInTimeAtoms(%s), looking in reader %s\n", name, reader->getPath() ); - bool isDylibReader = (reader->getInstallPath() != NULL); - if ( isDylibReader ? searchDylibs : searchArchives ) { - std::vector<class ObjectFile::Atom*>* atoms = reader->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - if ( !isDylibReader && fOptions.readerOptions().fTraceArchives ) { - logArchive(reader); - } - // if this is a weak definition in a dylib - if ( isDylibReader && (atoms->size() == 1) && (atoms->at(0)->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - // keep looking for a non-weak definition - } - else { - // found a definition, no need to search anymore - return atoms; - } - } - } - } - } - - // for two level namesapce, give all implicitly link dylibs a chance - if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) { - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->implicitlyLinked() ) { - //fprintf(stderr, "addJustInTimeAtoms(%s), looking in implicitly linked %s\n", name, it->second->getPath() ); - std::vector<class ObjectFile::Atom*>* 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<class ObjectFile::Atom*>* atoms = it->second->getJustInTimeAtomsFor(name); - if ( atoms != NULL ) { - this->addAtoms(*atoms); - //fprintf(stderr, "addJustInTimeAtoms(%s) => found in file %s\n", name, reader->getPath() ); - return atoms; // found a definition, no need to search anymore - } - } - } - } - - // writer creates a proxy in two cases: - // 1) ld -r is being used to create a .o file - // 2) -undefined dynamic_lookup is being used - // 3) -U _foo is being used - // 4) x86_64 kext bundle is being created - if ( (fOptions.outputKind() == Options::kObjectFile) - || ((fOptions.undefinedTreatment() != Options::kUndefinedError) && okToMakeProxy) - || (fOptions.someAllowedUndefines() && okToMakeProxy) - || (fOptions.outputKind() == Options::kKextBundle) ) { - ObjectFile::Atom* atom = fOutputFile->getUndefinedProxyAtom(name); - if ( atom != NULL ) { - this->addAtom(*atom); - return NULL; - } - } - //fprintf(stderr, "addJustInTimeAtoms(%s) => not found\n", name); - return NULL; -} - -void Linker::resolve(ObjectFile::Reference* reference) -{ - // look in global symbol table - const char* targetName = reference->getTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( target == NULL ) { - throwf("unexpected undefined symbol: %s", targetName); - } - reference->setTarget(*target, reference->getTargetOffset()); -} - -void Linker::resolveFrom(ObjectFile::Reference* reference) -{ - // handle references that have two (from and to) targets - const char* fromTargetName = reference->getFromTargetName(); - ObjectFile::Atom* fromTarget = fGlobalSymbolTable.find(fromTargetName); - if ( fromTarget == NULL ) { - throwf("unexpected undefined symbol: %s", fromTargetName); - } - reference->setFromTarget(*fromTarget); -} - - -void Linker::resolveReferences() -{ - // note: the atom list may grow during this loop as libraries supply needed atoms - for (unsigned int j=0; j < fAllAtoms.size(); ++j) { - ObjectFile::Atom* atom = fAllAtoms[j]; - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) - this->resolve(reference); - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) - this->resolveFrom(reference); - } - } -} - - -// used to remove stabs associated with atoms that won't be in output file -class NotInSet -{ -public: - NotInSet(std::set<ObjectFile::Atom*>& theSet) : fSet(theSet) {} - - bool operator()(const ObjectFile::Reader::Stab& stab) const { - if ( stab.atom == NULL ) - return false; // leave stabs that are not associated with any atome - else - return ( fSet.count(stab.atom) == 0 ); - } - -private: - std::set<ObjectFile::Atom*>& fSet; -}; - - -class NotLive -{ -public: - NotLive(std::set<ObjectFile::Atom*>& set) : fLiveAtoms(set) {} - - bool operator()(ObjectFile::Atom*& atom) const { - //if ( fLiveAtoms.count(atom) == 0 ) - // fprintf(stderr, "dead strip %s\n", atom->getDisplayName()); - return ( fLiveAtoms.count(atom) == 0 ); - } -private: - std::set<ObjectFile::Atom*>& fLiveAtoms; -}; - - -void Linker::addJustInTimeAtomsAndMarkLive(const char* name) -{ - //fprintf(stderr, "addJustInTimeAtomsAndMarkLive(%s)\n", name); - std::vector<class ObjectFile::Atom*>* atoms = this->addJustInTimeAtoms(name, true, true, true); - if ( atoms != NULL ) { - if ( fOptions.allGlobalsAreDeadStripRoots() ) { - for (std::vector<ObjectFile::Atom*>::iterator it=atoms->begin(); it != atoms->end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getScope() == ObjectFile::Atom::scopeGlobal ) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.referer = atom; - this->markLive(*atom, &rootChain); - } - } - } - delete atoms; - } -} - -void Linker::markLive(ObjectFile::Atom& atom, struct Linker::WhyLiveBackChain* previous) -{ - //fprintf(stderr, "markLive(%p)\n", &atom); - if ( fLiveAtoms.count(&atom) == 0 ) { - // if -why_live cares about this symbol, then dump chain - if ( (previous->referer != NULL) && fOptions.printWhyLive(previous->referer->getDisplayName()) ) { - int depth = 0; - for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { - for(int i=depth; i > 0; --i) - fprintf(stderr, " "); - fprintf(stderr, "%p %s from %s\n", p->referer, p->referer->getDisplayName(), p->referer->getFile()->getPath()); - } - } - // set up next chain - WhyLiveBackChain thisChain; - thisChain.previous = previous; - // this atom is live - fLiveAtoms.insert(&atom); - // update total size info (except for __ZEROPAGE atom) - if ( atom.getSegment().isContentReadable() ) { - fTotalSize += atom.getSize(); - if ( atom.isZeroFill() ) - fTotalZeroFillSize += atom.getSize(); - } - // and all atoms it references - std::vector<class ObjectFile::Reference*>& references = atom.getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator it=references.begin(); it != references.end(); it++) { - ObjectFile::Reference* reference = *it; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - // look in global symbol table - const char* targetName = reference->getTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { - // load archives or dylibs - this->addJustInTimeAtomsAndMarkLive(targetName); - } - // look again - target = fGlobalSymbolTable.find(targetName); - if ( target != NULL ) { - reference->setTarget(*target, reference->getTargetOffset()); - } - else { - // mark as undefined, for later error processing - fAtomsWithUnresolvedReferences.push_back(&atom); - fGlobalSymbolTable.require(targetName); - } - } - switch ( reference->getTargetBinding() ) { - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - thisChain.referer = &reference->getTarget(); - markLive(reference->getTarget(), &thisChain); - break; - case ObjectFile::Reference::kDontBind: - case ObjectFile::Reference::kUnboundByName: - // do nothing - break; - } - // do the same as above, for "from target" - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - // look in global symbol table - const char* targetName = reference->getFromTargetName(); - ObjectFile::Atom* target = fGlobalSymbolTable.find(targetName); - if ( (target == NULL) || (target->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) ) { - // load archives or dylibs - this->addJustInTimeAtomsAndMarkLive(targetName); - } - // look again - target = fGlobalSymbolTable.find(targetName); - if ( target != NULL ) { - reference->setFromTarget(*target); - } - else { - // mark as undefined, for later error processing - fGlobalSymbolTable.require(targetName); - } - } - switch ( reference->getFromTargetBinding() ) { - case ObjectFile::Reference::kBoundDirectly: - case ObjectFile::Reference::kBoundByName: - thisChain.referer = &reference->getFromTarget(); - markLive(reference->getFromTarget(), &thisChain); - break; - case ObjectFile::Reference::kUnboundByName: - case ObjectFile::Reference::kDontBind: - // do nothing - break; - } - } - } -} - - -void Linker::addLiveRoot(const char* name) -{ - ObjectFile::Atom* target = fGlobalSymbolTable.find(name); - if ( target == NULL ) { - this->addJustInTimeAtomsAndMarkLive(name); - target = fGlobalSymbolTable.find(name); - } - if ( target != NULL ) - fLiveRootAtoms.insert(target); -} - -void Linker::moveToFrontOfSection(ObjectFile::Atom* atom) -{ - // check if already moved to front - if ( fInitializerAtoms.find(atom) == fInitializerAtoms.end() ) { - // don't re-order initializers from .o files without MH_SUBSECTIONS_VIA_SYMBOLS - // since that could make all atoms in the file look like initializers - if ( atom->getFile()->canScatterAtoms() ) { - //fprintf(stdout, "marking as initializer: %s\n", atom->getDisplayName()); - fInitializerAtoms.insert(atom); - // mark all functions that this function references - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::const_iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Atom* childAtom = &((*rit)->getTarget()); - if ( childAtom != NULL ) { - if ( (*rit)->isBranch() ) { - this->moveToFrontOfSection(childAtom); - } - else if ( (childAtom->getName() != NULL) && (strncmp(childAtom->getName(), "___tcf_", 7) == 0) ) { - //fprintf(stdout, "marking as terminator: %s\n", childAtom->getDisplayName()); - fTerminatorAtoms.insert(childAtom); - } - } - } - } - } -} - -void Linker::deadStripResolve() -{ - // add main() to live roots - ObjectFile::Atom* entryPoint = this->entryPoint(false, true); - if ( entryPoint != NULL ) - fLiveRootAtoms.insert(entryPoint); - - // add dyld_stub_binding_helper/dyld_stub_binder to live roots - ObjectFile::Atom* dyldHelper = this->dyldClassicHelper(); - if ( dyldHelper != NULL ) - fLiveRootAtoms.insert(dyldHelper); - dyldHelper = this->dyldCompressedHelper(); - if ( dyldHelper != NULL ) - fLiveRootAtoms.insert(dyldHelper); - - // if using lazy dylib loading, add dyld_lazy_dylib_stub_binding_helper() to live roots - if ( fOptions.usingLazyDylibLinking() ) { - ObjectFile::Atom* dyldLazyDylibHelper = this->dyldLazyLibraryHelper(); - if ( dyldLazyDylibHelper != NULL ) - fLiveRootAtoms.insert(dyldLazyDylibHelper); - } - - // add -exported_symbols_list, -init, and -u entries to live roots - std::vector<const char*>& initialUndefines = fOptions.initialUndefines(); - for (std::vector<const char*>::iterator it=initialUndefines.begin(); it != initialUndefines.end(); it++) - addLiveRoot(*it); - - // if -exported_symbols_list that has wildcards, we need to find all matches and make them the roots - // <rdar://problem/5524973> - if ( fOptions.hasWildCardExportRestrictList() ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) - && (fDeadAtoms.count(atom) == 0) - && fOptions.shouldExport(atom->getName()) ) - fLiveRootAtoms.insert(atom); - } - } - - // in some cases, every global scope atom in initial .o files is a root - if ( fOptions.allGlobalsAreDeadStripRoots() ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) && (fDeadAtoms.count(atom) == 0) ) - fLiveRootAtoms.insert(atom); - } - } - - // mark all roots as live, and all atoms they reference - for (std::set<ObjectFile::Atom*>::iterator it=fLiveRootAtoms.begin(); it != fLiveRootAtoms.end(); it++) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.referer = *it; - markLive(**it, &rootChain); - } - - // it is possible that there are unresolved references that can be resolved now - // this can happen if the first reference to a common symbol in an archive. - // common symbols are not in the archive TOC, but the .o could have been pulled in later. - // <rdar://problem/4654131> ld64 while linking cc1 [ when dead_strip is ON] - for (std::vector<ObjectFile::Atom*>::iterator it=fAtomsWithUnresolvedReferences.begin(); it != fAtomsWithUnresolvedReferences.end(); it++) { - std::vector<class ObjectFile::Reference*>& references = (*it)->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - if ( reference->getTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getTargetName()); - if ( target != NULL ) { - reference->setTarget(*target, reference->getTargetOffset()); - fLiveAtoms.insert(target); - // by just adding this atom to fLiveAtoms set, we are assuming it has no - // references, which is true for commons. - if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) - warning("internal error %s is not a tentative definition", target->getDisplayName()); - } - } - if ( reference->getFromTargetBinding() == ObjectFile::Reference::kUnboundByName ) { - ObjectFile::Atom* target = fGlobalSymbolTable.find(reference->getFromTargetName()); - if ( target != NULL ) { - reference->setFromTarget(*target); - fLiveAtoms.insert(target); - // by just adding this atom to fLiveAtoms set, we are assuming it has no - // references, which is true for commons. - if ( target->getDefinitionKind() != ObjectFile::Atom::kTentativeDefinition ) - warning("internal error %s is not a tentative definition", target->getDisplayName()); - } - } - } - } - - // It is possible that some weak symbols were overridden by lazily load objects from archives - // and we have some atoms that still refer to the overridden ones. - // In that case we need to go back and rebind - if ( fAtomsOverriddenByLateLoads.size() > 0 ) { - for (std::set<ObjectFile::Atom*>::iterator it=fLiveAtoms.begin(); it != fLiveAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) { - ObjectFile::Reference* reference = *rit; - ObjectFile::Atom* toTarget = &reference->getTarget(); - if ( fAtomsOverriddenByLateLoads.count(toTarget) ) { - //fprintf(stderr, "change reference in %p from %p to %p\n", atom, toTarget, fGlobalSymbolTable.find(toTarget->getName())); - reference->setTarget(*fGlobalSymbolTable.find(toTarget->getName()), reference->getTargetOffset()); - } - ObjectFile::Atom* fromTarget = &reference->getFromTarget(); - if ( (fromTarget != NULL) && fAtomsOverriddenByLateLoads.count(fromTarget) ) { - //fprintf(stderr, "change from reference in %p from %p to %p\n", atom, fromTarget, fGlobalSymbolTable.find(fromTarget->getName())); - reference->setTarget(*fGlobalSymbolTable.find(fromTarget->getName()), reference->getFromTargetOffset()); - } - } - } - - // make sure overriders are live if the atom they overrid was live - for (std::set<ObjectFile::Atom*>::iterator it=fAtomsOverriddenByLateLoads.begin(); it != fAtomsOverriddenByLateLoads.end(); ++it) { - ObjectFile::Atom* overriderAtom = *it; - if ( fLiveAtoms.count(overriderAtom) ) { - WhyLiveBackChain rootChain; - rootChain.previous = NULL; - rootChain.referer = *it; - markLive(*fGlobalSymbolTable.find(overriderAtom->getName()), &rootChain); - } - } - - // remove overridden atoms from fLiveAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fAtomsOverriddenByLateLoads)), fAllAtoms.end()); - fAtomsOverriddenByLateLoads.clear(); - // remove dead atoms from fLiveAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), InSet(fDeadAtoms)), fAllAtoms.end()); - } - - // now remove all non-live atoms from fAllAtoms - fAllAtoms.erase(std::remove_if(fAllAtoms.begin(), fAllAtoms.end(), NotLive(fLiveAtoms)), fAllAtoms.end()); -} - -void Linker::checkObjC() -{ - // check dylibs - switch ( fCurrentObjCConstraint ) { - case ObjectFile::Reader::kObjcNone: - // can link against any dylib - break; - case ObjectFile::Reader::kObjcRetainRelease: - // cannot link against GC-only dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->explicitlyLinked() ) { - if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcGC ) - throwf("this linkage unit uses Retain/Release. It cannot link against the GC-only dylib: %s", it->second->getPath()); - } - } - break; - case ObjectFile::Reader::kObjcRetainReleaseOrGC: - // can link against GC or RR dylibs - break; - case ObjectFile::Reader::kObjcGC: - // cannot link against RR-only dylibs - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - if ( it->second->explicitlyLinked() ) { - if ( it->second->getObjCConstraint() == ObjectFile::Reader::kObjcRetainRelease ) - throwf("this linkage unit requires GC. It cannot link against Retain/Release dylib: %s", it->second->getPath()); - } - } - break; - } - - // synthesize __OBJC __image_info atom if needed - if ( fCurrentObjCConstraint != ObjectFile::Reader::kObjcNone ) { - this->addAtom(fOutputFile->makeObjcInfoAtom(fCurrentObjCConstraint, fObjcReplacmentClasses)); - } -} - - -static uint8_t pcRelKind(cpu_type_t arch) -{ - switch ( arch ) { - case CPU_TYPE_POWERPC: - return ppc::kPointerDiff32; - case CPU_TYPE_POWERPC64: - return ppc64::kPointerDiff32; - case CPU_TYPE_I386: - return x86::kPointerDiff; - case CPU_TYPE_X86_64: - return x86_64::kPointerDiff32; - case CPU_TYPE_ARM: - return arm::kPointerDiff; - } - throw "uknown architecture"; -} - -typedef uint8_t* (*oldcreatedof_func_t) (const char*, cpu_type_t, unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); -typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); - - -void Linker::processDTrace() -{ - // only make __dof section in final linked images - if ( fOptions.outputKind() == Options::kObjectFile ) - return; - - // scan all atoms looking for dtrace probes - std::vector<DTraceProbeInfo> probeSites; - std::vector<DTraceProbeInfo> isEnabledSites; - std::map<const ObjectFile::Atom*,CStringSet> atomToDtraceTypes; - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { - ObjectFile::Atom* atom = *it; - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) { - ObjectFile::Reference* ref = *rit; - if ( ref->getTargetBinding() == ObjectFile::Reference::kDontBind ) { - const char* probeName = ref->getTargetName(); - if ( probeName != NULL ) { - uint32_t offsetInAtom = ref->getFixUpOffset(); - if ( strncmp(probeName, "___dtrace_probe$", 16) == 0 ) - probeSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_isenabled$", 20) == 0 ) - isEnabledSites.push_back(DTraceProbeInfo(atom, offsetInAtom, probeName)); - else if ( strncmp(probeName, "___dtrace_", 10) == 0 ) - atomToDtraceTypes[atom].insert(probeName); - } - } - } - } - - // if no probes, we're done - if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) ) - return; - - // partition probes by provider name - // The symbol names looks like: - // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] - // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] - ProviderToProbes providerToProbes; - std::vector<DTraceProbeInfo> emptyList; - for(std::vector<DTraceProbeInfo>::iterator it = probeSites.begin(); it != probeSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[16]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); - } - } - } - for(std::vector<DTraceProbeInfo>::iterator it = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) { - // ignore probes in functions that were coalesed away rdar://problem/5628149 - if ( fDeadAtoms.count((ObjectFile::Atom*)(it->atom)) == 0 ) { - const char* providerStart = &it->probeName[20]; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char providerName[providerEnd-providerStart+1]; - strlcpy(providerName, providerStart, providerEnd-providerStart+1); - ProviderToProbes::iterator pos = providerToProbes.find(providerName); - if ( pos == providerToProbes.end() ) { - const char* dup = strdup(providerName); - providerToProbes[dup] = emptyList; - } - providerToProbes[providerName].push_back(*it); - } - } - } - - // create a DOF section for each provider - int dofIndex=1; - CStringSet sectionNamesUsed; - for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { - const char* providerName = pit->first; - const std::vector<DTraceProbeInfo>& probes = pit->second; - - // open library and find dtrace_create_dof() - void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); - if ( handle == NULL ) - throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); - createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); - if ( pCreateDOF == NULL ) - throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); - // build list of typedefs/stability infos for this provider - CStringSet types; - for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) { - std::map<const ObjectFile::Atom*,CStringSet>::iterator pos = atomToDtraceTypes.find(it->atom); - if ( pos != atomToDtraceTypes.end() ) { - for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { - const char* providerStart = strchr(*sit, '$')+1; - const char* providerEnd = strchr(providerStart, '$'); - if ( providerEnd != NULL ) { - char aProviderName[providerEnd-providerStart+1]; - strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); - if ( strcmp(aProviderName, providerName) == 0 ) - types.insert(*sit); - } - } - } - } - int typeCount = types.size(); - const char* typeNames[typeCount]; - //fprintf(stderr, "types for %s:\n", providerName); - uint32_t index = 0; - for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { - typeNames[index] = *it; - //fprintf(stderr, "\t%s\n", *it); - ++index; - } - - // build list of probe/isenabled sites - const uint32_t probeCount = probes.size(); - const char* probeNames[probeCount]; - const char* funtionNames[probeCount]; - uint64_t offsetsInDOF[probeCount]; - index = 0; - for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) { - probeNames[index] = it->probeName; - funtionNames[index] = it->atom->getName(); - offsetsInDOF[index] = 0; - ++index; - } - //fprintf(stderr, "calling libtrace to create DOF\n"); - //for(uint32_t i=0; i < probeCount; ++i) - // fprintf(stderr, " [%u]\t %s\t%s\n", i, probeNames[i], funtionNames[i]); - // call dtrace library to create DOF section - size_t dofSectionSize; - uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); - if ( p != NULL ) { - char sectionName[18]; - strcpy(sectionName, "__dof_"); - strlcpy(§ionName[6], providerName, 10); - // create unique section name so each DOF is in its own section - if ( sectionNamesUsed.count(sectionName) != 0 ) { - sectionName[15] = '0'; - sectionName[16] = '\0'; - while ( sectionNamesUsed.count(sectionName) != 0 ) - ++sectionName[15]; - } - sectionNamesUsed.insert(sectionName); - char symbolName[strlen(providerName)+64]; - sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); - opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName, - "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName); - fNextInputOrdinal += dofSectionSize; - // add references - for (uint32_t i=0; i < probeCount; ++i) { - uint64_t offset = offsetsInDOF[i]; - //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); - if ( offset > dofSectionSize ) - throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); - reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0); - } - this->addAtoms(reader->getAtoms()); - } - else { - throw "error creating dtrace DOF section"; - } - } -} - - -static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName) -{ - if ( objectFileLeafName == NULL ) - return true; - const char* atomFullPath = atom->getFile()->getPath(); - const char* lastSlash = strrchr(atomFullPath, '/'); - if ( lastSlash != NULL ) { - if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) - return true; - } - else { - if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) - return true; - } - return false; -} - - -ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol) -{ - ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName); - if ( atom != NULL ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) - return atom; - } - else { - // slow case. The requested symbol is not in symbol table, so might be static function - static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols; - static bool built = false; - // build a hash_map the first time - if ( !built ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - const char* name = atom->getName(); - if ( name != NULL) { - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // static function or data - SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name); - if ( pos == hashTableOfTranslationUnitScopedSymbols.end() ) - hashTableOfTranslationUnitScopedSymbols[name] = atom; - else - hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL - } - } - } - //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size()); - built = true; - } - - // look for name in hashTableOfTranslationUnitScopedSymbols - SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName); - if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) { - if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { - //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); - return pos->second; - } - if ( pos->second == NULL ) - // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atom = *it; - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - const char* name = atom->getName(); - if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) { - if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { - if ( fOptions.printOrderFileStatistics() ) - warning("%s specified in order_file but it exists in multiple .o files. " - "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); - return atom; - } - } - } - } - } - - } - return NULL; -} - - -void Linker::sortSections() -{ - Section::assignIndexes(fOptions.outputKind() == Options::kObjectFile); -} - - -// -// Linker::sortAtoms() -// -// The purpose of this method is to take the graph of all Atoms and produce an ordered -// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must -// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified -// in an order_file are seqenced as in the order_file and before Atoms not specified, -// 4) Atoms in the same section from the same .o file should be contiguous and sequenced -// in the same order they were in the .o file, 5) Atoms in the same Section but which came -// from different .o files should be sequenced in the same order that the .o files -// were passed to the linker (i.e. command line order). -// -// The way this is implemented is that the linker passes a "base ordinal" to each Reader -// as it is constructed. The reader should construct it Atoms so that calling getOrdinal() -// on its atoms returns a contiguous range of values starting at the base ordinal. Then -// sorting is just sorting by section, then by ordinal. -// -// If an order_file is specified, it gets more complicated. First, an override-ordinal map -// is created. It causes the sort routine to ignore the value returned by getOrdinal() and -// use the override value instead. Next some Atoms must be layed out consecutively -// (e.g. hand written assembly that does not end with return, but rather falls into -// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of -// kFollowOn refernces produces "clusters" of atoms that must stay together. -// If an order_file tries to move one atom, it may need to move a whole cluster. The -// algorithm to do this models clusters using two maps. The "starts" maps maps any -// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a -// cluster to the next Atom in the cluster. With this in place, while processing an -// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is -// given ordinal overrides. -// -void Linker::sortAtoms() -{ - fStartSortTime = mach_absolute_time(); - // if -order_file is used, build map of atom ordinal overrides - std::map<const ObjectFile::Atom*, uint32_t>* ordinalOverrideMap = NULL; - std::map<const ObjectFile::Atom*, uint32_t> 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<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnStarts; - std::map<const ObjectFile::Atom*, const ObjectFile::Atom*> followOnNexts; - for (std::vector<ObjectFile::Atom*>::iterator ait=fAllAtoms.begin(); ait != fAllAtoms.end(); ait++) { - ObjectFile::Atom* atom = *ait; - std::vector<class ObjectFile::Reference*>& references = atom->getReferences(); - for (std::vector<ObjectFile::Reference*>::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<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator startFrom = followOnStarts.find(atom); - std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator startTo = followOnStarts.find(targetAtom); - if ( (startFrom == followOnStarts.end()) && (startTo == followOnStarts.end()) ) { - // this is first time we've seen either atom, make simple cluster of the two - if ( log ) fprintf(stderr, " new cluster\n"); - followOnStarts[atom] = atom; - followOnStarts[targetAtom] = atom; - followOnNexts[atom] = targetAtom; - followOnNexts[targetAtom] = NULL; - } - else if ( (startFrom != followOnStarts.end()) && (startTo == followOnStarts.end()) && (followOnNexts[atom] == NULL) ) { - // atom is at end of an existing cluster, so append target to end of cluster - if ( log ) fprintf(stderr, " end of cluster starting with %s\n", followOnStarts[atom]->getDisplayName()); - followOnNexts[atom] = targetAtom; - followOnNexts[targetAtom] = NULL; - followOnStarts[targetAtom] = followOnStarts[atom]; - } - else { - // gerneral case of inserting into an existing cluster - if ( followOnNexts[atom] != NULL ) { - // an atom with two follow-ons is illegal - warning("can't order %s because both %s and %s must follow it", - atom->getDisplayName(), targetAtom->getDisplayName(), followOnNexts[atom]->getDisplayName()); - } - else { - // there already exists an atom that says target must be its follow-on - const ObjectFile::Atom* originalStart = startTo->second; - const ObjectFile::Atom* originalPrevious = originalStart; - while ( followOnNexts[originalPrevious] != targetAtom ) - originalPrevious = followOnNexts[originalPrevious]; - bool otherIsAlias = (originalPrevious->getSize() == 0); - bool thisIsAlias = (atom->getSize() == 0); - if ( !otherIsAlias && !thisIsAlias ) { - warning("can't order %s because both %s and %s must preceed it", - targetAtom->getDisplayName(), originalPrevious->getDisplayName(), atom->getDisplayName()); - } - else if ( otherIsAlias ) { - if ( originalPrevious == originalStart ) { - // other is alias at start of cluster, make this the new start of cluster - if ( log ) fprintf(stderr, " becomes new start of cluster previous starting with %s\n", originalStart->getDisplayName()); - followOnNexts[atom] = originalPrevious; - for(const ObjectFile::Atom* nextAtom = atom; nextAtom != NULL; nextAtom = followOnNexts[nextAtom]) - followOnStarts[nextAtom] = atom; - } - else { - // other is alias in middle of cluster, insert new atom before it - if ( log ) fprintf(stderr, " insert into cluster starting with %s before alias %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); - followOnStarts[atom] = originalStart; - followOnNexts[atom] = originalPrevious; - for(const ObjectFile::Atom* a = originalStart; a != NULL; a = followOnNexts[a]) { - if ( followOnNexts[a] == originalPrevious ) { - followOnNexts[a] = atom; - break; - } - } - } - } - else { - // this is alias, so it can go inbetween originalPrevious and targetAtom - if ( log ) fprintf(stderr, " insert into cluster starting with %s after %s\n", originalStart->getDisplayName(), originalPrevious->getDisplayName()); - followOnStarts[atom] = originalStart; - followOnNexts[atom] = followOnNexts[originalPrevious]; - followOnNexts[originalPrevious] = atom; - } - } - } - } - } - } - - if ( log ) { - for(std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::iterator it = followOnStarts.begin(); it != followOnStarts.end(); ++it) - fprintf(stderr, "start %s -> %s\n", it->first->getDisplayName(), it->second->getDisplayName()); - - for(std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::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<Options::OrderedSymbol>& orderedSymbols = fOptions.orderedSymbols(); - for(std::vector<Options::OrderedSymbol>::iterator it = orderedSymbols.begin(); it != orderedSymbols.end(); ++it) { - ObjectFile::Atom* atom = this->findAtom(*it); - if ( atom != NULL ) { - std::map<const ObjectFile::Atom*, const ObjectFile::Atom*>::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<const ObjectFile::Atom*, uint32_t>::iterator pos = theOrdinalOverrideMap.find(nextAtom); - if ( pos == theOrdinalOverrideMap.end() ) { - theOrdinalOverrideMap[nextAtom] = index++; - if (log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->getDisplayName(), nextAtom->getFile()->getPath()); - } - else { - if (log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", - atom->getDisplayName(), index, followOnStarts[atom]->getDisplayName(), theOrdinalOverrideMap[atom] ); - } - } - } - else { - theOrdinalOverrideMap[atom] = index; - if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath()); - } - ++matchCount; - } - else { - if ( fOptions.printOrderFileStatistics() ) { - if ( it->objectFileName == NULL ) - warning("can't find match for order_file entry: %s", it->symbolName); - else - warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName); - } - } - ++index; - } - if ( fOptions.printOrderFileStatistics() && (fOptions.orderedSymbols().size() != matchCount) ) { - warning("only %u out of %lu order_file symbols were applicable", matchCount, fOptions.orderedSymbols().size() ); - } - } - - // sort atoms - std::sort(fAllAtoms.begin(), fAllAtoms.end(), Linker::AtomSorter(ordinalOverrideMap, fInitializerAtoms, fTerminatorAtoms)); - - //fprintf(stderr, "Sorted atoms:\n"); - //for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - // fprintf(stderr, "\t%s, %u %s\t%s\n", (*it)->getSectionName(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath()); - //} -} - - -// make sure given addresses are within reach of branches, etc -void Linker::tweakLayout() -{ - // > 2GB images need their large zero fill atoms sorted to the end to keep access with +/- 2GB - if ( fTotalSize > 0x7F000000 ) { - fBiggerThanTwoGigOutput = true; - - if ( (fTotalSize-fTotalZeroFillSize) > 0x7F000000 ) - throwf("total output size exceeds 2GB (%lldMB)", (fTotalSize-fTotalZeroFillSize)/(1024*1024)); - - // move very large (>1MB) zero fill atoms to a new section at very end of __DATA segment - Section* hugeZeroFills = Section::find("__huge", "__DATA", true, true); - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->isZeroFill() && (atom->getSize() > 1024*1024) && (strcmp(atom->getSegment().getName(), "__DATA") == 0) ) - atom->setSection(hugeZeroFills); - } - } - - // move all initializers to start of __text section - if ( fOptions.readerOptions().fAutoOrderInitializers ) { - // move -init function to front of __text - if ( fOptions.initFunctionName() != NULL ) { - ObjectFile::Atom* initAtom = fGlobalSymbolTable.find(fOptions.initFunctionName()); - if ( initAtom == NULL ) - throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); - moveToFrontOfSection(initAtom); - } - - // move all functions pointed to by __mod_init_func section to front of __text - Section* initSection = Section::find("__mod_init_func", "__DATA", false, true, false); - if ( initSection != NULL ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { - if ( (*it)->getSection() == initSection ) { - std::vector<class ObjectFile::Reference*>& references = (*it)->getReferences(); - if ( references.size() == 1 ) - moveToFrontOfSection(&(references[0]->getTarget())); - } - } - } - } - - // move atoms with relocations to start of __DATA,__data section - // <rdar://problem/6061558> linker should order __DATA segment to reduce dyld dirtied pages - if ( fOptions.orderData() ) { - bool slideable = false; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kPreload: - case Options::kObjectFile: - case Options::kKextBundle: - slideable = false; - break; - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - slideable = true; - break; - } - const bool hasPreferredLoadAddress = (fOptions.baseAddress() != 0); - Section* dataSection = Section::find("__data", "__DATA", false, true, false); - if ( dataSection != NULL ) { - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); ++it) { - ObjectFile::Atom* dataAtom = *it; - if ( dataAtom->getSection() == dataSection ) { - std::vector<class ObjectFile::Reference*>& references = dataAtom->getReferences(); - if ( references.size() > 0 ) { - if ( slideable && !hasPreferredLoadAddress ) { - // in a slidable image dyld will need to rebase and bind so any references will need runtime fixups - // if image has preferred base address, assume it will load there and not rebase - moveToFrontOfSection(dataAtom); - } - else { - // in a non-slideable image, dyld will only do binding, so only references to - // symbols in another dylib will need runtime fixups - //fprintf(stderr, "reference from atom %s\n", dataAtom->getDisplayName()); - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - //fprintf(stderr, "\t%d %s\n", reference->getTarget().getDefinitionKind(), reference->getTarget().getDisplayName()); - if ( (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (reference->getTarget().getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - moveToFrontOfSection(dataAtom); - break; - } - } - } - } - } - } - } - } - -} - - -void Linker::writeDotOutput() -{ - const char* dotOutFilePath = fOptions.dotOutputFile(); - if ( dotOutFilePath != NULL ) { - FILE* out = fopen(dotOutFilePath, "w"); - if ( out != NULL ) { - // print header - fprintf(out, "digraph dg\n{\n"); - fprintf(out, "\tconcentrate = true;\n"); - fprintf(out, "\trankdir = LR;\n"); - - // print each atom as a node - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getFile() != fOutputFile ) { - const char* name = atom->getDisplayName(); - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fprintf(out, "\taddr%p [ shape = plaintext, label = \"%s\" ];\n", atom, name); - } - else if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) { - char cstring[atom->getSize()+2]; - atom->copyRawContent((uint8_t*)cstring); - fprintf(out, "\taddr%p [ label = \"string: '", atom); - for (const char* s=cstring; *s != '\0'; ++s) { - if ( *s == '\n' ) - fprintf(out, "\\\\n"); - else - fputc(*s, out); - } - fprintf(out, "'\" ];\n"); - } - else { - fprintf(out, "\taddr%p [ label = \"%s\" ];\n", atom, name); - } - } - } - fprintf(out, "\n"); - - // print each reference as an edge - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* fromAtom = *it; - if ( fromAtom->getFile() != fOutputFile ) { - std::vector<ObjectFile::Reference*>& references = fromAtom->getReferences(); - std::set<ObjectFile::Atom*> seenTargets; - for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); rit++) { - ObjectFile::Reference* reference = *rit; - ObjectFile::Atom* toAtom = &(reference->getTarget()); - if ( seenTargets.count(toAtom) == 0 ) { - seenTargets.insert(toAtom); - fprintf(out, "\taddr%p -> addr%p;\n", fromAtom, toAtom); - } - } - } - } - fprintf(out, "\n"); - - // push all imports to bottom of graph - fprintf(out, "{ rank = same; "); - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - ObjectFile::Atom* atom = *it; - if ( atom->getFile() != fOutputFile ) - if ( (atom->getDefinitionKind() == ObjectFile::Atom::kExternalDefinition) - || (atom->getDefinitionKind() == ObjectFile::Atom::kExternalWeakDefinition) ) { - fprintf(out, "addr%p; ", atom); - } - } - fprintf(out, "};\n "); - - // print footer - fprintf(out, "}\n"); - fclose(out); - } - else { - warning("could not write dot output file: %s", dotOutFilePath); - } - } -} - -ObjectFile::Atom* Linker::entryPoint(bool orInit, bool searchArchives) -{ - // if main executable, find entry point atom - ObjectFile::Atom* entryPoint = NULL; - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kPreload: - entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); - if ( (entryPoint == NULL) && searchArchives ) { - // <rdar://problem/7043256> ld64 can not find a -e entry point from an archive - this->addJustInTimeAtoms(fOptions.entryName(), false, true, false); - entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); - } - if ( entryPoint == NULL ) { - throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); - } - break; - case Options::kDynamicLibrary: - if ( orInit && (fOptions.initFunctionName() != NULL) ) { - entryPoint = fGlobalSymbolTable.find(fOptions.initFunctionName()); - if ( entryPoint == NULL ) { - throwf("could not find -init function: \"%s\"", fOptions.initFunctionName()); - } - } - break; - case Options::kObjectFile: - case Options::kDynamicBundle: - case Options::kKextBundle: - entryPoint = NULL; - break; - } - return entryPoint; -} - -ObjectFile::Atom* Linker::dyldClassicHelper() -{ - if ( fOptions.makeClassicDyldInfo() ) - return fGlobalSymbolTable.find("dyld_stub_binding_helper"); - else - return NULL; -} - -ObjectFile::Atom* Linker::dyldCompressedHelper() -{ - if ( fOptions.makeCompressedDyldInfo() ) { - // dyld_stub_binder is in libSystem.B.dylib - ObjectFile::Atom* atom = fGlobalSymbolTable.find("dyld_stub_binder"); - if ( atom == NULL ) { - this->addJustInTimeAtoms("dyld_stub_binder", true, false, true); - } - atom = fGlobalSymbolTable.find("dyld_stub_binder"); - return atom; - } - else - return NULL; -} - -ObjectFile::Atom* Linker::dyldLazyLibraryHelper() -{ - return fGlobalSymbolTable.find("dyld_lazy_dylib_stub_binding_helper"); -} - -const char* Linker::assureFullPath(const char* path) -{ - if ( path[0] == '/' ) - return path; - char cwdbuff[MAXPATHLEN]; - if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { - char* result; - asprintf(&result, "%s/%s", cwdbuff, path); - if ( result != NULL ) - return result; - } - return path; -} - - -// -// The stab strings are of the form: -// <name> ':' <type-code> <number-pari> -// but the <name> contain a colon. -// For C++ <name> may contain a double colon (e.g. std::string:f(0,1) ) -// For Objective-C name may contain a colon instead square bracket (e.g. [Foo doit:]:f(0,1) ) -// -const char* Linker::truncateStabString(const char* str) -{ - enum { start, inObjc } state = start; - for (const char* s = str; *s != 0; ++s) { - char c = *s; - switch (state) { - case start: - if ( c == '[' ) { - state = inObjc; - } - else { - if ( c == ':' ) { - if ( s[1] == ':' ) { - ++s; - } - else { - // found colon - // Duplicate strndup behavior here. - int trunStrLen = s-str+2; - char* temp = new char[trunStrLen+1]; - memcpy(temp, str, trunStrLen); - temp[trunStrLen] = '\0'; - return temp; - } - } - } - break; - case inObjc: - if ( c == ']' ) { - state = start; - } - break; - } - } - // malformed - return str; -} - - -bool Linker::minimizeStab(ObjectFile::Reader::Stab& stab) -{ - switch(stab.type){ - case N_GSYM: - case N_STSYM: - case N_LCSYM: - case N_FUN: - // these all need truncated strings - stab.string = truncateStabString(stab.string); - return true; - case N_SO: - case N_OSO: - case N_OPT: - case N_SOL: - // these are included in the minimal stabs, but they keep their full string - return true; - default: - return false; - } -} - - -struct HeaderRange { - std::vector<ObjectFile::Reader::Stab>::iterator begin; - std::vector<ObjectFile::Reader::Stab>::iterator end; - int parentRangeIndex; - uint32_t sum; - bool sumPrecomputed; - bool useEXCL; - bool cannotEXCL; // because of SLINE, etc stabs -}; - - -typedef __gnu_cxx::hash_map<const char*, std::vector<uint32_t>, __gnu_cxx::hash<const char*>, CStringEquals> PathToSums; - -// hash table that maps header path to a vector of known checksums for that path -static PathToSums sKnownBINCLs; - - -void Linker::collectStabs(ObjectFile::Reader* reader, std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals) -{ - const bool log = false; - bool minimal = ( fOptions.readerOptions().fDebugInfoStripping == ObjectFile::ReaderOptions::kDebugInfoMinimal ); - std::vector<class ObjectFile::Reader::Stab>* readerStabs = reader->getStabs(); - if ( readerStabs == NULL ) - return; - - if ( log ) fprintf(stderr, "processesing %lu stabs for %s\n", readerStabs->size(), reader->getPath()); - std::vector<HeaderRange> ranges; - int curRangeIndex = -1; - int count = 0; - ObjectFile::Atom* atomWithLowestOrdinal = NULL; - ObjectFile::Atom* atomWithHighestOrdinal = NULL; - uint32_t highestOrdinal = 0; - uint32_t lowestOrdinal = UINT_MAX; - std::vector<std::pair<ObjectFile::Atom*,ObjectFile::Atom*> > soRanges; - // 1) find all (possibly nested) BINCL/EINCL ranges and their checksums - // 2) find all SO/SO ranges and the first/last atom own by a FUN stab therein - for(std::vector<class ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - ++count; - switch ( it->type ) { - case N_BINCL: - { - HeaderRange range; - range.begin = it; - range.end = readerStabs->end(); - range.parentRangeIndex = curRangeIndex; - range.sum = it->value; - range.sumPrecomputed = (range.sum != 0); - range.useEXCL = false; - range.cannotEXCL = false; - curRangeIndex = ranges.size(); - if ( log ) fprintf(stderr, "[%d]BINCL %s\n", curRangeIndex, it->string); - ranges.push_back(range); - } - break; - case N_EINCL: - if ( curRangeIndex == -1 ) { - warning("EINCL missing BINCL in %s", reader->getPath()); - } - else { - ranges[curRangeIndex].end = it+1; - if ( log ) fprintf(stderr, "[%d->%d]EINCL %s\n", curRangeIndex, ranges[curRangeIndex].parentRangeIndex, it->string); - curRangeIndex = ranges[curRangeIndex].parentRangeIndex; - } - break; - case N_FUN: - { - std::map<const class ObjectFile::Atom*, uint32_t>::iterator pos = atomOrdinals.find(it->atom); - if ( pos != atomOrdinals.end() ) { - uint32_t ordinal = pos->second; - if ( ordinal > highestOrdinal ) { - highestOrdinal = ordinal; - atomWithHighestOrdinal = it->atom; - } - if ( ordinal < lowestOrdinal ) { - lowestOrdinal = ordinal; - atomWithLowestOrdinal = it->atom; - } - } - } - // fall through - case N_BNSYM: - case N_ENSYM: - case N_LBRAC: - case N_RBRAC: - case N_SLINE: - case N_STSYM: - case N_LCSYM: - if ( curRangeIndex != -1 ) { - ranges[curRangeIndex].cannotEXCL = true; - if ( fOptions.warnStabs() ) - warning("cannot do BINCL/EINCL optimzation because of stabs kinds in %s for %s\n", ranges[curRangeIndex].begin->string, reader->getPath()); - } - break; - case N_SO: - if ( (it->string != NULL) && (strlen(it->string) > 0) ) { - // start SO, reset hi/low FUN tracking - atomWithLowestOrdinal = NULL; - atomWithHighestOrdinal = NULL; - highestOrdinal = 0; - lowestOrdinal = UINT_MAX; - } - else { - // end SO, record hi/low atoms for this SO range - soRanges.push_back(std::make_pair<ObjectFile::Atom*,ObjectFile::Atom*>(atomWithLowestOrdinal, atomWithHighestOrdinal)); - } - // fall through - default: - if ( curRangeIndex != -1 ) { - if ( ! ranges[curRangeIndex].sumPrecomputed ) { - uint32_t sum = 0; - const char* s = it->string; - char c; - while ( (c = *s++) != 0 ) { - sum += c; - // don't checkusm first number (file index) after open paren in string - if ( c == '(' ) { - while(isdigit(*s)) - ++s; - } - } - ranges[curRangeIndex].sum += sum; - } - } - - } - } - if ( log ) fprintf(stderr, "processesed %d stabs for %s\n", count, reader->getPath()); - if ( curRangeIndex != -1 ) - warning("BINCL (%s) missing EINCL in %s", ranges[curRangeIndex].begin->string, reader->getPath()); - - // if no BINCLs - if ( ranges.size() == 0 ) { - unsigned int soIndex = 0; - for(std::vector<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - // copy minimal or all stabs - ObjectFile::Reader::Stab stab = *it; - if ( !minimal || minimizeStab(stab) ) { - if ( stab.type == N_SO ) { - if ( soIndex < soRanges.size() ) { - if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { - // starting SO is associated with first atom - stab.atom = soRanges[soIndex].first; - } - else { - // ending SO is associated with last atom - stab.atom = soRanges[soIndex].second; - ++soIndex; - } - } - } - fStabs.push_back(stab); - } - } - return; - } - - //fprintf(stderr, "BINCL/EINCL info for %s\n", reader->getPath()); - //for(std::vector<HeaderRange>::iterator it=ranges.begin(); it != ranges.end(); ++it) { - // fprintf(stderr, "%08X %s\n", it->sum, it->begin->string); - //} - - // see if any of these BINCL/EINCL ranges have already been seen and therefore can be replaced with EXCL - for(std::vector<HeaderRange>::iterator it=ranges.begin(); it != ranges.end(); ++it) { - if ( ! it->cannotEXCL ) { - const char* header = it->begin->string; - uint32_t sum = it->sum; - PathToSums::iterator pos = sKnownBINCLs.find(header); - if ( pos != sKnownBINCLs.end() ) { - std::vector<uint32_t>& sums = pos->second; - for(std::vector<uint32_t>::iterator sit=sums.begin(); sit != sums.end(); ++sit) { - if (*sit == sum) { - //fprintf(stderr, "use EXCL for %s in %s\n", header, reader->getPath()); - it->useEXCL = true; - break; - } - } - if ( ! it->useEXCL ) { - // have seen this path, but not this checksum - //fprintf(stderr, "registering another checksum %08X for %s\n", sum, header); - sums.push_back(sum); - } - } - else { - // have not seen this path, so add to known BINCLs - std::vector<uint32_t> empty; - sKnownBINCLs[header] = empty; - sKnownBINCLs[header].push_back(sum); - //fprintf(stderr, "registering checksum %08X for %s\n", sum, header); - } - } - } - - // add a new set of stabs with BINCL/EINCL runs that have been seen before, replaced with EXCLs - curRangeIndex = -1; - const int maxRangeIndex = ranges.size(); - int soIndex = 0; - for(std::vector<ObjectFile::Reader::Stab>::iterator it=readerStabs->begin(); it != readerStabs->end(); ++it) { - switch ( it->type ) { - case N_BINCL: - for(int i=curRangeIndex+1; i < maxRangeIndex; ++i) { - if ( ranges[i].begin == it ) { - curRangeIndex = i; - HeaderRange& range = ranges[curRangeIndex]; - ObjectFile::Reader::Stab stab = *it; - stab.value = range.sum; // BINCL and EXCL have n_value set to checksum - if ( range.useEXCL ) - stab.type = N_EXCL; // transform BINCL into EXCL - if ( !minimal ) - fStabs.push_back(stab); - break; - } - } - break; - case N_EINCL: - if ( curRangeIndex != -1 ) { - if ( !ranges[curRangeIndex].useEXCL && !minimal ) - fStabs.push_back(*it); - curRangeIndex = ranges[curRangeIndex].parentRangeIndex; - } - break; - default: - if ( (curRangeIndex == -1) || !ranges[curRangeIndex].useEXCL ) { - ObjectFile::Reader::Stab stab = *it; - if ( !minimal || minimizeStab(stab) ) { - if ( stab.type == N_SO ) { - if ( (stab.string != NULL) && (strlen(stab.string) > 0) ) { - // starting SO is associated with first atom - stab.atom = soRanges[soIndex].first; - } - else { - // ending SO is associated with last atom - stab.atom = soRanges[soIndex].second; - ++soIndex; - } - } - fStabs.push_back(stab); - } - } - } - } - -} - - -// used to prune out atoms that don't need debug notes generated -class NoDebugNoteAtom -{ -public: - NoDebugNoteAtom(const std::map<class ObjectFile::Reader*, uint32_t>& readersWithDwarfOrdinals) - : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals) {} - - bool operator()(const ObjectFile::Atom* atom) const { - if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) - return true; - if ( atom->getName() == NULL ) - return true; - if ( fReadersWithDwarfOrdinals.find(atom->getFile()) == fReadersWithDwarfOrdinals.end() ) - return true; - return false; - } - -private: - const std::map<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals; -}; - -// used to sort atoms with debug notes -class ReadersWithDwarfSorter -{ -public: - ReadersWithDwarfSorter(const std::map<class ObjectFile::Reader*, uint32_t>& readersWithDwarfOrdinals, - const std::map<const class ObjectFile::Atom*, uint32_t>& atomOrdinals) - : fReadersWithDwarfOrdinals(readersWithDwarfOrdinals), fAtomOrdinals(atomOrdinals) {} - - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) const - { - // first sort by reader - unsigned int leftReaderIndex = fReadersWithDwarfOrdinals.find(left->getFile())->second; - unsigned int rightReaderIndex = fReadersWithDwarfOrdinals.find(right->getFile())->second; - if ( leftReaderIndex != rightReaderIndex ) - return (leftReaderIndex < rightReaderIndex); - - // then sort by atom ordinal - unsigned int leftAtomIndex = fAtomOrdinals.find(left)->second; - unsigned int rightAtomIndex = fAtomOrdinals.find(right)->second; - return leftAtomIndex < rightAtomIndex; - } - -private: - const std::map<class ObjectFile::Reader*, uint32_t>& fReadersWithDwarfOrdinals; - const std::map<const class ObjectFile::Atom*, uint32_t>& fAtomOrdinals; -}; - - - - - -void Linker::synthesizeDebugNotes(std::vector<class ObjectFile::Atom*>& allAtomsByReader) -{ - // synthesize "debug notes" and add them to master stabs vector - const char* dirPath = NULL; - const char* filename = NULL; - bool wroteStartSO = false; - bool useZeroOSOModTime = (getenv("RC_RELEASE") != NULL); - __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> seenFiles; - for (std::vector<ObjectFile::Atom*>::iterator it=allAtomsByReader.begin(); it != allAtomsByReader.end(); it++) { - ObjectFile::Atom* atom = *it; - const char* newDirPath; - const char* newFilename; - //fprintf(stderr, "debug note for %s\n", atom->getDisplayName()); - if ( atom->getTranslationUnitSource(&newDirPath, &newFilename) ) { - // need SO's whenever the translation unit source file changes - if ( newFilename != filename ) { - // gdb like directory SO's to end in '/', but dwarf DW_AT_comp_dir usually does not have trailing '/' - if ( (newDirPath != NULL) && (strlen(newDirPath) > 1 ) && (newDirPath[strlen(newDirPath)-1] != '/') ) - asprintf((char**)&newDirPath, "%s/", newDirPath); - if ( filename != NULL ) { - // translation unit change, emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } - // new translation unit, emit start SO's - ObjectFile::Reader::Stab dirPathStab; - dirPathStab.atom = NULL; - dirPathStab.type = N_SO; - dirPathStab.other = 0; - dirPathStab.desc = 0; - dirPathStab.value = 0; - dirPathStab.string = newDirPath; - fStabs.push_back(dirPathStab); - ObjectFile::Reader::Stab fileStab; - fileStab.atom = NULL; - fileStab.type = N_SO; - fileStab.other = 0; - fileStab.desc = 0; - fileStab.value = 0; - fileStab.string = newFilename; - fStabs.push_back(fileStab); - // Synthesize OSO for start of file - ObjectFile::Reader::Stab objStab; - objStab.atom = NULL; - objStab.type = N_OSO; - // <rdar://problem/6337329> linker should put cpusubtype in n_sect field of nlist entry for N_OSO debug note entries - objStab.other = atom->getFile()->updateCpuConstraint(0); - objStab.desc = 1; - objStab.value = useZeroOSOModTime ? 0 : atom->getFile()->getModificationTime(); - objStab.string = assureFullPath(atom->getFile()->getPath()); - fStabs.push_back(objStab); - wroteStartSO = true; - // add the source file path to seenFiles so it does not show up in SOLs - seenFiles.insert(newFilename); - } - filename = newFilename; - dirPath = newDirPath; - if ( atom->getSegment().isContentExecutable() && (strncmp(atom->getSectionName(), "__text", 6) == 0) ) { - // Synthesize BNSYM and start FUN stabs - ObjectFile::Reader::Stab beginSym; - beginSym.atom = atom; - beginSym.type = N_BNSYM; - beginSym.other = 1; - beginSym.desc = 0; - beginSym.value = 0; - beginSym.string = ""; - fStabs.push_back(beginSym); - ObjectFile::Reader::Stab startFun; - startFun.atom = atom; - startFun.type = N_FUN; - startFun.other = 1; - startFun.desc = 0; - startFun.value = 0; - startFun.string = atom->getName(); - fStabs.push_back(startFun); - // Synthesize any SOL stabs needed - std::vector<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo(); - if ( lineInfo != NULL ) { - const char* curFile = NULL; - for (std::vector<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { - if ( it->fileName != curFile ) { - if ( seenFiles.count(it->fileName) == 0 ) { - seenFiles.insert(it->fileName); - ObjectFile::Reader::Stab sol; - sol.atom = 0; - sol.type = N_SOL; - sol.other = 0; - sol.desc = 0; - sol.value = 0; - sol.string = it->fileName; - fStabs.push_back(sol); - } - curFile = it->fileName; - } - } - } - // Synthesize end FUN and ENSYM stabs - ObjectFile::Reader::Stab endFun; - endFun.atom = atom; - endFun.type = N_FUN; - endFun.other = 0; - endFun.desc = 0; - endFun.value = 0; - endFun.string = ""; - fStabs.push_back(endFun); - ObjectFile::Reader::Stab endSym; - endSym.atom = atom; - endSym.type = N_ENSYM; - endSym.other = 1; - endSym.desc = 0; - endSym.value = 0; - endSym.string = ""; - fStabs.push_back(endSym); - } - else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableNotIn ) { - // no stabs for atoms that would not be in the symbol table - } - else if ( atom->getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableInAsAbsolute ) { - // no stabs for absolute symbols - } - else if ( (strcmp(atom->getSectionName(), "__eh_frame") == 0) ) { - // no stabs for .eh atoms - } - else if ( (strncmp(atom->getName(), "__dtrace_probe$", 15) == 0) ) { - // no stabs for old style dtrace probes - } - else { - ObjectFile::Reader::Stab globalsStab; - const char* name = atom->getName(); - if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) { - // Synthesize STSYM stab for statics - globalsStab.atom = atom; - globalsStab.type = N_STSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - else { - // Synthesize GSYM stab for other globals - globalsStab.atom = atom; - globalsStab.type = N_GSYM; - globalsStab.other = 1; - globalsStab.desc = 0; - globalsStab.value = 0; - globalsStab.string = name; - fStabs.push_back(globalsStab); - } - } - } - } - - if ( wroteStartSO ) { - // emit ending SO - ObjectFile::Reader::Stab endFileStab; - endFileStab.atom = NULL; - endFileStab.type = N_SO; - endFileStab.other = 1; - endFileStab.desc = 0; - endFileStab.value = 0; - endFileStab.string = ""; - fStabs.push_back(endFileStab); - } -} - - - - -void Linker::collectDebugInfo() -{ - std::map<const class ObjectFile::Atom*, uint32_t> atomOrdinals; - fStartDebugTime = mach_absolute_time(); - if ( fOptions.readerOptions().fDebugInfoStripping != ObjectFile::ReaderOptions::kDebugInfoNone ) { - - // determine mixture of stabs and dwarf - bool someStabs = false; - bool someDwarf = false; - for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - switch ( reader->getDebugInfoKind() ) { - case ObjectFile::Reader::kDebugInfoNone: - break; - case ObjectFile::Reader::kDebugInfoStabs: - someStabs = true; - break; - case ObjectFile::Reader::kDebugInfoDwarf: - someDwarf = true; - fCreateUUID = true; - break; - case ObjectFile::Reader::kDebugInfoStabsUUID: - someStabs = true; - fCreateUUID = true; - break; - default: - throw "Unhandled type of debug information"; - } - } - } - - if ( someDwarf || someStabs ) { - // try to minimize re-allocations - fStabs.reserve(1024); - - // make mapping from atoms to ordinal - uint32_t ordinal = 1; - for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) { - atomOrdinals[*it] = ordinal++; - } - } - - // process all dwarf .o files as a batch - if ( someDwarf ) { - // make mapping from readers with dwarf to ordinal - std::map<class ObjectFile::Reader*, uint32_t> readersWithDwarfOrdinals; - uint32_t readerOrdinal = 1; - for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( (reader != NULL) && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoDwarf) ) { - readersWithDwarfOrdinals[reader] = readerOrdinal++; - } - } - - // make a vector of atoms - std::vector<class ObjectFile::Atom*> allAtomsByReader(fAllAtoms.begin(), fAllAtoms.end()); - // remove those not from a reader that has dwarf - allAtomsByReader.erase(std::remove_if(allAtomsByReader.begin(), allAtomsByReader.end(), - NoDebugNoteAtom(readersWithDwarfOrdinals)), allAtomsByReader.end()); - // sort by reader then atom ordinal - std::sort(allAtomsByReader.begin(), allAtomsByReader.end(), ReadersWithDwarfSorter(readersWithDwarfOrdinals, atomOrdinals)); - // add debug notes for each atom - this->synthesizeDebugNotes(allAtomsByReader); - } - - // process all stabs .o files one by one - if ( someStabs ) { - // get stabs from each reader, in command line order - for (std::vector<class ObjectFile::Reader*>::iterator it=fReadersThatHaveSuppliedAtoms.begin(); - it != fReadersThatHaveSuppliedAtoms.end(); - it++) { - ObjectFile::Reader* reader = *it; - if ( reader != NULL ) { - switch ( reader->getDebugInfoKind() ) { - case ObjectFile::Reader::kDebugInfoDwarf: - case ObjectFile::Reader::kDebugInfoNone: - // do nothing - break; - case ObjectFile::Reader::kDebugInfoStabs: - case ObjectFile::Reader::kDebugInfoStabsUUID: - collectStabs(reader, atomOrdinals); - break; - default: - throw "Unhandled type of debug information"; - } - } - } - // remove stabs associated with atoms that won't be in output - std::set<class ObjectFile::Atom*> allAtomsSet; - allAtomsSet.insert(fAllAtoms.begin(), fAllAtoms.end()); - fStabs.erase(std::remove_if(fStabs.begin(), fStabs.end(), NotInSet(allAtomsSet)), fStabs.end()); - } - } -} - -void Linker::writeOutput() -{ - // <rdar://problem/6933931> ld -r of empty .o file should preserve sub-type - // <rdar://problem/7049478> empty dylib should have subtype from command line - if ( fOptions.preferSubArchitecture() && (fOptions.architecture() == CPU_TYPE_ARM) ) { - fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)fOptions.subArchitecture(); - } - - if ( fOptions.forceCpuSubtypeAll() ) - fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; - - fStartWriteTime = mach_absolute_time(); - // tell writer about each segment's atoms - fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), - fCreateUUID, fCanScatter, - fCurrentCpuConstraint, - fRegularDefAtomsThatOverrideADylibsWeakDef, - fGlobalSymbolTable.hasExternalWeakDefinitions()); -} - -const char* Linker::fileArch(const void* p) -{ - const uint8_t* bytes = (uint8_t*)p; - const char* result; - result = mach_o::relocatable::Reader<ppc>::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader<ppc64>::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader<x86>::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader<x86_64>::fileKind(bytes); - if ( result != NULL ) - return result; - result = mach_o::relocatable::Reader<arm>::fileKind(bytes); - if ( result != NULL ) - return result; - - result = lto::Reader::fileKind(bytes); - if ( result != NULL ) - return result; - - return "unsupported file format"; -} - -ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) -{ - // map in whole file - uint64_t len = info.fileLen; - int fd = ::open(info.path, O_RDONLY, 0); - if ( fd == -1 ) - throwf("can't open file, errno=%d", errno); - if ( info.fileLen < 20 ) - throw "file too small"; - - uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); - if ( p == (uint8_t*)(-1) ) - throwf("can't map file, errno=%d", errno); - - // if fat file, skip to architecture we want - // Note: fat header is always big-endian - bool isFatFile = false; - const fat_header* fh = (fat_header*)p; - if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - isFatFile = true; - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - uint32_t sliceToUse; - bool sliceFound = false; - if ( fOptions.preferSubArchitecture() ) { - // first try to find a slice that match cpu-type and cpu-sub-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( (OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture) - && (OSSwapBigToHostInt32(archs[i].cpusubtype) == (uint32_t)fOptions.subArchitecture()) ) { - sliceToUse = i; - sliceFound = true; - break; - } - } - } - if ( !sliceFound ) { - // look for any slice that matches just cpu-type - for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)fArchitecture ) { - sliceToUse = i; - sliceFound = true; - break; - } - } - } - if ( sliceFound ) { - uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); - len = OSSwapBigToHostInt32(archs[sliceToUse].size); - // if requested architecture is page aligned within fat file, then remap just that portion of file - if ( (fileOffset & 0x00000FFF) == 0 ) { - // unmap whole file - munmap((caddr_t)p, info.fileLen); - // re-map just part we need - p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); - if ( p == (uint8_t*)(-1) ) - throwf("can't re-map file, errno=%d", errno); - } - else { - p = &p[fileOffset]; - } - } - } - ::close(fd); - - bool objSubtypeMustMatch = (fOptions.preferSubArchitecture() && !fOptions.allowSubArchitectureMismatches()); - switch (fArchitecture) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Reader<ppc>::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader<ppc>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader<ppc>::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader<ppc>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader<ppc>::validFile(p, len) ) - return this->addArchive(new archive::Reader<ppc>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Reader<ppc64>::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader<ppc64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader<ppc64>::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader<ppc64>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader<ppc64>::validFile(p, len) ) - return this->addArchive(new archive::Reader<ppc64>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Reader<x86>::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader<x86>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader<x86>::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader<x86>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader<x86>::validFile(p, len) ) - return this->addArchive(new archive::Reader<x86>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Reader<x86_64>::validFile(p) ) - return this->addObject(new mach_o::relocatable::Reader<x86_64>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader<x86_64>::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader<x86_64>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader<x86_64>::validFile(p, len) ) - return this->addArchive(new archive::Reader<x86_64>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Reader<arm>::validFile(p, objSubtypeMustMatch, fOptions.subArchitecture()) ) - return this->addObject(new mach_o::relocatable::Reader<arm>::Reader(p, info.path, info.modTime, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( mach_o::dylib::Reader<arm>::validFile(p, info.options.fBundleLoader) ) - return this->addDylib(new mach_o::dylib::Reader<arm>::Reader(p, len, info.path, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - else if ( archive::Reader<arm>::validFile(p, len) ) - return this->addArchive(new archive::Reader<arm>::Reader(p, len, info.path, info.modTime, info.options, fOptions.readerOptions(), fNextInputOrdinal), info, len); - break; - } - -#if LTO_SUPPORT - if ( lto::Reader::validFile(p, len, fArchitecture) ) { - return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len); - } - else if ( lto::Reader::fileKind((uint8_t*)p) != NULL ) { - if ( lto::Reader::loaded() ) { - throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); - } - else { - const char* libLTO = "libLTO.dylib"; - char ldPath[PATH_MAX]; - char tmpPath[PATH_MAX]; - char libLTOPath[PATH_MAX]; - uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { - if ( realpath(ldPath, tmpPath) != NULL ) { - char* lastSlash = strrchr(tmpPath, '/'); - if ( lastSlash != NULL ) - strcpy(lastSlash, "/../lib/libLTO.dylib"); - libLTO = tmpPath; - if ( realpath(tmpPath, libLTOPath) != NULL ) - libLTO = libLTOPath; - } - } - throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO); - } - } -#endif - // error handling - if ( ((fat_header*)p)->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - throwf("missing required architecture %s in file", fArchitectureName); - } - else { - if ( isFatFile ) - throwf("file is universal but does not contain a(n) %s slice", fArchitectureName); - else - throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); - } -} - -void Linker::logDylib(ObjectFile::Reader* reader, bool indirect) -{ - if ( fOptions.readerOptions().fTraceDylibs ) { - const char* fullPath = reader->getPath(); - char realName[MAXPATHLEN]; - if ( realpath(fullPath, realName) != NULL ) - fullPath = realName; - if ( indirect ) - logTraceInfo("[Logging for XBS] Used indirect dynamic library: %s\n", fullPath); - else - logTraceInfo("[Logging for XBS] Used dynamic library: %s\n", fullPath); - } -} - - - -ObjectFile::Reader* Linker::findDylib(const char* installPath, const char* fromPath) -{ - //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); - InstallNameToReader::iterator pos = fDylibMap.find(installPath); - if ( pos != fDylibMap.end() ) { - return pos->second; - } - else { - // allow -dylib_path option to override indirect library to use - for (std::vector<Options::DylibOverride>::iterator dit = fOptions.dylibOverrides().begin(); dit != fOptions.dylibOverrides().end(); ++dit) { - if ( strcmp(dit->installName,installPath) == 0 ) {\ - try { - Options::FileInfo info = fOptions.findFile(dit->useInstead); - ObjectFile::Reader* reader = this->createReader(info); - fDylibMap[strdup(installPath)] = reader; - this->logDylib(reader, true); - return reader; - } - catch (const char* msg) { - warning("ignoring -dylib_file option, %s", msg); - } - } - } - char newPath[MAXPATHLEN]; - // handle @loader_path - if ( strncmp(installPath, "@loader_path/", 13) == 0 ) { - strcpy(newPath, fromPath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &installPath[13]); - else - strcpy(newPath, &installPath[13]); - installPath = newPath; - } - // note: @executable_path case is handled inside findFileUsingPaths() - // search for dylib using -F and -L paths - Options::FileInfo info = fOptions.findFileUsingPaths(installPath); - try { - ObjectFile::Reader* reader = this->createReader(info); - fDylibMap[strdup(installPath)] = reader; - this->logDylib(reader, true); - return reader; - } - catch (const char* msg) { - throwf("in %s, %s", info.path, msg); - } - } -} - - -void Linker::processDylibs() -{ - fAllDirectDylibsLoaded = true; - - // mark all dylibs initially specified as required and check if they can be used - for (InstallNameToReader::iterator it=fDylibMap.begin(); it != fDylibMap.end(); it++) { - it->second->setExplicitlyLinked(); - this->checkDylibClientRestrictions(it->second); - } - - // keep processing dylibs until no more dylibs are added - unsigned long lastMapSize = 0; - while ( lastMapSize != fDylibMap.size() ) { - lastMapSize = fDylibMap.size(); - // can't iterator fDylibMap while modifying it, so use temp buffer - std::vector<ObjectFile::Reader*> 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<ObjectFile::Reader*>::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<class ObjectFile::Reader*>::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<ObjectFile::Reader*,LibraryOptions>::iterator pos = fDylibOptionsMap.find(reader); - if ( pos != fDylibOptionsMap.end() ) { - pos->second.fReExport = true; - } - } - } - } - } - } - -} - - - -void Linker::createReaders() -{ - fStartCreateReadersTime = mach_absolute_time(); - std::vector<Options::FileInfo>& files = fOptions.getInputFiles(); - const int count = files.size(); - if ( count == 0 ) - throw "no object files specified"; - // add all direct object, archives, and dylibs - for (int i=0; i < count; ++i) { - Options::FileInfo& entry = files[i]; - // ignore /usr/lib/dyld on command line in crt.o build - if ( strcmp(entry.path, "/usr/lib/dyld") != 0 ) { - try { - this->addInputFile(this->createReader(entry), entry); - } - catch (const char* msg) { - if ( (strstr(msg, "architecture") != NULL) && !fOptions.errorOnOtherArchFiles() ) { - if ( fOptions.ignoreOtherArchInputFiles() ) { - // ignore, because this is about an architecture not in use - } - else { - warning("in %s, %s", entry.path, msg); - } - } - else { - throwf("in %s, %s", entry.path, msg); - } - } - } - } - - this->processDylibs(); -} - - - -ObjectFile::Reader* Linker::addArchive(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - fNextInputOrdinal += mappedLen; - // remember which readers are archives because they are logged differently - fArchiveReaders.insert(reader); - - // update stats - fTotalArchiveSize += mappedLen; - ++fTotalArchivesLoaded; - return reader; -} - -ObjectFile::Reader* Linker::addObject(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - fNextInputOrdinal += mappedLen; - // any .o files that don't have MH_SUBSECTIONS_VIA_SYMBOLS, that means a generated .o file can't - if ( (fOptions.outputKind() == Options::kObjectFile) && !reader->canScatterAtoms() ) - fCanScatter = false; - - // update stats - fTotalObjectSize += mappedLen; - ++fTotalObjectLoaded; - return reader; -} - - -void Linker::checkDylibClientRestrictions(ObjectFile::Reader* reader) -{ - // Check for any restrictions on who can link with this dylib - const char* readerParentName = reader->parentUmbrella() ; - std::vector<const char*>* clients = reader->getAllowableClients(); - if ( (readerParentName != NULL) || (clients != NULL) ) { - // only dylibs that are in an umbrella or have a client list need verification - const char* installName = fOptions.installPath(); - const char* installNameLastSlash = strrchr(installName, '/'); - bool isParent = false; - bool isSibling = false; - bool isAllowableClient = false; - // There are three cases: - if ( (readerParentName != NULL) && (installNameLastSlash != NULL) ) { - // case 1) The dylib has a parent umbrella, and we are creating the parent umbrella - isParent = ( strcmp(&installNameLastSlash[1], readerParentName) == 0 ); - - // hack to support umbrella variants that encode the variant name in the install name - // e.g. CoreServices_profile - if ( !isParent ) { - const char* underscore = strchr(&installNameLastSlash[1], '_'); - if ( underscore != NULL ) { - isParent = ( strncmp(&installNameLastSlash[1], readerParentName, underscore-installNameLastSlash-1) == 0 ); - } - } - - // case 2) The dylib has a parent umbrella, and we are creating a sibling with the same parent - isSibling = ( (fOptions.umbrellaName() != NULL) && (strcmp(fOptions.umbrellaName(), readerParentName) == 0) ); - } - - if ( !isParent && !isSibling && (clients != NULL) ) { - // case 3) the dylib has a list of allowable clients, and we are creating one of them - const char* clientName = fOptions.clientName(); - int clientNameLen = 0; - if ( clientName != NULL ) { - // use client name as specified on command line - clientNameLen = strlen(clientName); - } - else { - // infer client name from output path (e.g. xxx/libfoo_variant.A.dylib --> foo, Bar.framework/Bar_variant --> Bar) - clientName = installName; - clientNameLen = strlen(clientName); - // starts after last slash - if ( installNameLastSlash != NULL ) - clientName = &installNameLastSlash[1]; - if ( strncmp(clientName, "lib", 3) == 0 ) - clientName = &clientName[3]; - // up to first dot - const char* firstDot = strchr(clientName, '.'); - if ( firstDot != NULL ) - clientNameLen = firstDot - clientName; - // up to first underscore - const char* firstUnderscore = strchr(clientName, '_'); - if ( (firstUnderscore != NULL) && ((firstUnderscore - clientName) < clientNameLen) ) - clientNameLen = firstUnderscore - clientName; - } - - // Use clientName to check if this dylib is able to link against the allowable clients. - for (std::vector<const char*>::iterator it = clients->begin(); it != clients->end(); it++) { - if ( strncmp(*it, clientName, clientNameLen) == 0 ) - isAllowableClient = true; - } - } - - if ( !isParent && !isSibling && !isAllowableClient ) { - if ( readerParentName != NULL ) { - throwf("cannot link directly with %s. Link against the umbrella framework '%s.framework' instead.", - reader->getPath(), readerParentName); - } - else { - throwf("cannot link directly with %s", reader->getPath()); - } - } - } -} - - -ObjectFile::Reader* Linker::addDylib(ObjectFile::Reader* reader, const Options::FileInfo& info, uint64_t mappedLen) -{ - switch ( fOptions.outputKind() ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - break; - case Options::kStaticExecutable: - case Options::kDyld: - case Options::kPreload: - case Options::kObjectFile: - case Options::kKextBundle: - warning("unexpected dylib (%s) on link line", reader->getPath()); - break; - } - - fNextInputOrdinal += mappedLen; - if ( (reader->getInstallPath() == NULL) && !info.options.fBundleLoader ) { - // this is a "blank" stub - // silently ignore it - return reader; - } - // add to map of loaded dylibs - const char* installPath = reader->getInstallPath(); - if ( installPath != NULL ) { - InstallNameToReader::iterator pos = fDylibMap.find(installPath); - if ( pos == fDylibMap.end() ) { - fDylibMap[strdup(installPath)] = reader; - } - else { - InstallNameToReader::iterator pos2 = fDylibMap.find(reader->getPath()); - if ( pos2 == fDylibMap.end() ) - fDylibMap[strdup(reader->getPath())] = reader; - else - warning("duplicate dylib %s", reader->getPath()); - } - } - else if ( info.options.fBundleLoader ) - fBundleLoaderReader = reader; - - // log direct readers - if ( !fAllDirectDylibsLoaded ) - this->logDylib(reader, false); - - // update stats - ++fTotalDylibsLoaded; - - return reader; -} - - -void Linker::logTraceInfo (const char* format, ...) -{ - static int trace_file = -1; - char trace_buffer[MAXPATHLEN * 2]; - char *buffer_ptr; - int length; - ssize_t amount_written; - const char *trace_file_path = fOptions.readerOptions().fTraceOutputFile; - - if(trace_file == -1) { - if(trace_file_path != NULL) { - trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); - if(trace_file == -1) - throwf("Could not open or create trace file: %s", trace_file_path); - } - else { - trace_file = fileno(stderr); - } - } - - va_list ap; - va_start(ap, format); - length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); - va_end(ap); - buffer_ptr = trace_buffer; - - while(length > 0) { - amount_written = write(trace_file, buffer_ptr, length); - if(amount_written == -1) - /* Failure to write shouldn't fail the build. */ - return; - buffer_ptr += amount_written; - length -= amount_written; - } -} - - - -void Linker::createWriter() -{ - fStartCreateWriterTime = mach_absolute_time(); - - // make a vector out of all required dylibs in fDylibMap - std::vector<ExecutableFile::DyLibUsed> dynamicLibraries; - // need to preserve command line order - for (std::vector<class ObjectFile::Reader*>::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<ExecutableFile::DyLibUsed>::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<ObjectFile::Reader*,LibraryOptions>::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<ppc>(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_POWERPC64: - this->setOutputFile(new mach_o::executable::Writer<ppc64>(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_I386: - this->setOutputFile(new mach_o::executable::Writer<x86>(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_X86_64: - this->setOutputFile(new mach_o::executable::Writer<x86_64>(path, fOptions, dynamicLibraries)); - break; - case CPU_TYPE_ARM: - this->setOutputFile(new mach_o::executable::Writer<arm>(path, fOptions, dynamicLibraries)); - break; - default: - throw "unknown architecture"; - } -} - - -Linker::SymbolTable::SymbolTable(Linker& owner) - : fOwner(owner), fRequireCount(0), fHasExternalTentativeDefinitions(false), fHasExternalWeakDefinitions(false) -{ -} - -void Linker::SymbolTable::require(const char* name) -{ - //fprintf(stderr, "require(%s)\n", name); - Mapper::iterator pos = fTable.find(name); - if ( pos == fTable.end() ) { - fTable[name] = NULL; - ++fRequireCount; - } -} - -// convenience labels for 2-dimensional switch statement -enum AllDefinitionCombinations { - kRegAndReg = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kRegAndWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kRegAndTent = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kRegAndExtern = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kRegAndExternWeak = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kRegAndAbsolute = (ObjectFile::Atom::kRegularDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kWeakAndReg = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kWeakAndWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kWeakAndTent = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kWeakAndExtern = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kWeakAndExternWeak = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kWeakAndAbsolute = (ObjectFile::Atom::kWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kTentAndReg = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kTentAndWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kTentAndTent = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kTentAndExtern = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kTentAndExternWeak = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kTentAndAbsolute = (ObjectFile::Atom::kTentativeDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kExternAndReg = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kExternAndWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kExternAndTent = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kExternAndExtern = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kExternAndExternWeak = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kExternAndAbsolute = (ObjectFile::Atom::kExternalDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kExternWeakAndReg = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kRegularDefinition, - kExternWeakAndWeak = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kWeakDefinition, - kExternWeakAndTent = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kTentativeDefinition, - kExternWeakAndExtern = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalDefinition, - kExternWeakAndExternWeak= (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kExternWeakAndAbsolute = (ObjectFile::Atom::kExternalWeakDefinition << 3) | ObjectFile::Atom::kAbsoluteSymbol, - kAbsoluteAndReg = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kRegularDefinition, - kAbsoluteAndWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kWeakDefinition, - kAbsoluteAndTent = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kTentativeDefinition, - kAbsoluteAndExtern = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalDefinition, - kAbsoluteAndExternWeak = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kExternalWeakDefinition, - kAbsoluteAndAbsolute = (ObjectFile::Atom::kAbsoluteSymbol << 3) | ObjectFile::Atom::kAbsoluteSymbol -}; - -bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom) -{ - bool useNew = true; - bool checkVisibilityMismatch = false; - const char* name = newAtom.getName(); - //fprintf(stderr, "map.add(%s => %p from %s)\n", name, &newAtom, newAtom.getFile()->getPath()); - Mapper::iterator pos = fTable.find(name); - ObjectFile::Atom* existingAtom = NULL; - if ( pos != fTable.end() ) - existingAtom = pos->second; - if ( existingAtom != NULL ) { - // already have atom with same name in symbol table - switch ( (AllDefinitionCombinations)((existingAtom->getDefinitionKind() << 3) | newAtom.getDefinitionKind()) ) { - case kRegAndReg: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kRegAndWeak: - // ignore new weak atom, because we already have a non-weak one - useNew = false; - break; - case kRegAndTent: - // ignore new tentative atom, because we already have a regular one - useNew = false; - checkVisibilityMismatch = true; - if ( newAtom.getSize() > existingAtom->getSize() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "is smaller than the real definition of size %llu from %s", - newAtom.getDisplayName(), newAtom.getSize(), newAtom.getFile()->getPath(), - existingAtom->getSize(), existingAtom->getFile()->getPath()); - } - break; - case kRegAndExtern: - // ignore external atom, because we already have a one - useNew = false; - break; - case kRegAndExternWeak: - // ignore external atom, because we already have a one - useNew = false; - break; - case kRegAndAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case kWeakAndReg: - // replace existing weak atom with regular one - break; - case kWeakAndWeak: - // have another weak atom, use whichever has largest alignment requirement - // because codegen of some client may require alignment - useNew = ( newAtom.getAlignment().trailingZeros() > existingAtom->getAlignment().trailingZeros() ); - checkVisibilityMismatch = true; - break; - case kWeakAndTent: - // replace existing weak atom with tentative one ??? - break; - case kWeakAndExtern: - // keep weak atom, at runtime external one may override - useNew = false; - break; - case kWeakAndExternWeak: - // keep weak atom, at runtime external one may override - useNew = false; - break; - case kWeakAndAbsolute: - // replace existing weak atom with absolute one - break; - case kTentAndReg: - // replace existing tentative atom with regular one - checkVisibilityMismatch = true; - if ( newAtom.getSize() < existingAtom->getSize() ) { - warning("for symbol %s tentative definition of size %llu from %s is " - "being replaced by a real definition of size %llu from %s", - newAtom.getDisplayName(), existingAtom->getSize(), existingAtom->getFile()->getPath(), - newAtom.getSize(), newAtom.getFile()->getPath()); - } - break; - case kTentAndWeak: - // replace existing tentative atom with weak one ??? - break; - case kTentAndTent: - // use largest - checkVisibilityMismatch = true; - if ( newAtom.getSize() < existingAtom->getSize() ) { - useNew = false; - } - else { - if ( newAtom.getAlignment().trailingZeros() < existingAtom->getAlignment().trailingZeros() ) - warning("alignment lost in merging tentative definition %s", newAtom.getDisplayName()); - } - break; - case kTentAndExtern: - case kTentAndExternWeak: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing common symbol %s from %s with true definition from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - existingAtom->getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - } - break; - case kTentAndAbsolute: - // replace tentative with absolute (can't size check because absolutes have no size) - break; - case kExternAndReg: - // replace external atom with regular one - break; - case kExternAndWeak: - // replace external atom with weak one - break; - case kExternAndTent: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing defintion of %s from dylib %s with common symbol from %s", - newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - } - break; - case kExternAndExtern: - throwf("duplicate symbol %s in %s and %s\n", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kExternAndExternWeak: - // keep strong dylib atom, ignore weak one - useNew = false; - break; - case kExternAndAbsolute: - // replace external atom with absolute one - break; - case kExternWeakAndReg: - // replace existing weak external with regular - break; - case kExternWeakAndWeak: - // replace existing weak external with weak (let dyld decide at runtime which to use) - break; - case kExternWeakAndTent: - // a tentative definition and a dylib definition, so commons-mode decides how to handle - switch ( fOwner.fOptions.commonsMode() ) { - case Options::kCommonsIgnoreDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("using common symbol %s from %s and ignoring defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - case Options::kCommonsOverriddenByDylibs: - if ( fOwner.fOptions.warnCommons() ) - warning("replacing defintion of %s from dylib %s with common symbol from %s", - newAtom.getName(), existingAtom->getFile()->getPath(), newAtom.getFile()->getPath()); - useNew = false; - break; - case Options::kCommonsConflictsDylibsError: - throwf("common symbol %s from %s conflicts with defintion from dylib %s", - newAtom.getName(), newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - } - break; - case kExternWeakAndExtern: - // replace existing weak external with external - break; - case kExternWeakAndExternWeak: - // keep existing external weak - useNew = false; - break; - case kExternWeakAndAbsolute: - // replace existing weak external with absolute - break; - case kAbsoluteAndReg: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - case kAbsoluteAndWeak: - // ignore new weak atom, because we already have a non-weak one - useNew = false; - break; - case kAbsoluteAndTent: - // ignore new tentative atom, because we already have a regular one - useNew = false; - break; - case kAbsoluteAndExtern: - // ignore external atom, because we already have a one - useNew = false; - break; - case kAbsoluteAndExternWeak: - // ignore external atom, because we already have a one - useNew = false; - break; - case kAbsoluteAndAbsolute: - throwf("duplicate symbol %s in %s and %s", name, newAtom.getFile()->getPath(), existingAtom->getFile()->getPath()); - break; - } - } - if ( (existingAtom != NULL) && checkVisibilityMismatch && (newAtom.getScope() != existingAtom->getScope()) ) { - warning("%s has different visibility (%s) in %s and (%s) in %s", - newAtom.getDisplayName(), (newAtom.getScope() == 1 ? "hidden" : "default"), newAtom.getFile()->getPath(), (existingAtom->getScope() == 1 ? "hidden" : "default"), existingAtom->getFile()->getPath()); - } - if ( useNew ) { - fTable[name] = &newAtom; - if ( existingAtom != NULL ) { - fOwner.markDead(existingAtom); - if ( fOwner.fInitialLoadsDone ) { - //fprintf(stderr, "existing %p %s overridden by %p\n", existingAtom, existingAtom->getName(), &newAtom); - fOwner.fAtomsOverriddenByLateLoads.insert(existingAtom); - } - } - if ( newAtom.getScope() == ObjectFile::Atom::scopeGlobal ) { - switch ( newAtom.getDefinitionKind() ) { - case ObjectFile::Atom::kTentativeDefinition: - fHasExternalTentativeDefinitions = true; - ++fRequireCount; // added a tentative definition means loadUndefines() needs to continue - break; - case ObjectFile::Atom::kWeakDefinition: - if ( newAtom.getSymbolTableInclusion() == ObjectFile::Atom::kSymbolTableIn ) - fHasExternalWeakDefinitions = true; - break; - case ObjectFile::Atom::kExternalDefinition: - case ObjectFile::Atom::kExternalWeakDefinition: - ++fDylibSymbolCount; - break; - default: - break; - } - } - } - else { - fOwner.markDead(&newAtom); - } - return useNew; -} - - - -ObjectFile::Atom* Linker::SymbolTable::find(const char* name) -{ - Mapper::iterator pos = fTable.find(name); - if ( pos != fTable.end() ) { - return pos->second; - } - return NULL; -} - -void Linker::SymbolTable::erase(const char* name) { - fTable.erase(name); -} - -void Linker::SymbolTable::getUndefinesNames(std::vector<const char*>& undefines) -{ - for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { - if ( it->second == NULL ) { - undefines.push_back(it->first); - } - } -} - -void Linker::SymbolTable::getTentativesNames(std::vector<const char*>& tents) -{ - for (Mapper::iterator it=fTable.begin(); it != fTable.end(); it++) { - if ( it->second != NULL ) { - if ( (it->second->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition) - && (it->second->getScope() == ObjectFile::Atom::scopeGlobal) ) { - tents.push_back(it->first); - } - } - } -} - - - -bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) -{ - if ( left == right ) - return false; - - // first sort by section order (which is already sorted by segment) - unsigned int leftSectionIndex = left->getSection()->getIndex(); - unsigned int rightSectionIndex = right->getSection()->getIndex(); - if ( leftSectionIndex != rightSectionIndex) - return (leftSectionIndex < rightSectionIndex); - - // magic section$start symbol always sorts to the start of its section - if ( left->getContentType() == ObjectFile::Atom::kSectionStart ) - return true; - if ( right->getContentType() == ObjectFile::Atom::kSectionStart ) - return false; - - // if a -order_file is specified, then sorting is altered to sort those symbols first - if ( fOverriddenOrdinalMap != NULL ) { - std::map<const ObjectFile::Atom*, uint32_t>::iterator leftPos = fOverriddenOrdinalMap->find(left); - std::map<const ObjectFile::Atom*, uint32_t>::iterator rightPos = fOverriddenOrdinalMap->find(right); - std::map<const ObjectFile::Atom*, uint32_t>::iterator end = fOverriddenOrdinalMap->end(); - if ( leftPos != end ) { - if ( rightPos != end ) { - // both left and right are overridden, so compare overridden ordinals - return leftPos->second < rightPos->second; - } - else { - // left is overridden and right is not, so left < right - return true; - } - } - else { - if ( rightPos != end ) { - // right is overridden and left is not, so right < left - return false; - } - else { - // neither are overridden, do default sort - // fall into default sorting below - } - } - } - - // magic section$end symbol always sorts to the end of its section - if ( left->getContentType() == ObjectFile::Atom::kSectionEnd ) - return false; - if ( right->getContentType() == ObjectFile::Atom::kSectionEnd ) - return true; - - // the __common section can have real or tentative definitions - // we want the real ones to sort before tentative ones - bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); - bool rightIsTent = (right->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); - if ( leftIsTent != rightIsTent ) - return rightIsTent; - - // initializers are auto sorted to start of section - if ( !fInitializerSet.empty() ) { - bool leftFirst = (fInitializerSet.count(left) != 0); - bool rightFirst = (fInitializerSet.count(right) != 0); - if ( leftFirst != rightFirst ) - return leftFirst; - } - - // terminators are auto sorted to end of section - if ( !fTerminatorSet.empty() ) { - bool leftLast = (fTerminatorSet.count(left) != 0); - bool rightLast = (fTerminatorSet.count(right) != 0); - if ( leftLast != rightLast ) - return rightLast; - } - - // lastly sort by atom ordinal. this is already sorted by .o order - return left->getOrdinal() < right->getOrdinal(); -} - - -int main(int argc, const char* argv[]) -{ - const char* archName = NULL; - bool showArch = false; - bool archInferred = false; - try { - // create linker object given command line arguments - Linker ld(argc, argv); - - // save error message prefix - archName = ld.architectureName(); - archInferred = ld.isInferredArchitecture(); - showArch = ld.showArchitectureInErrors(); - - // open all input files - ld.createReaders(); - - // open output file - ld.createWriter(); - - // do linking - ld.link(); - } - catch (const char* msg) { - if ( archInferred ) - fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); - else if ( showArch ) - fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); - else - fprintf(stderr, "ld: %s\n", msg); - return 1; - } - - return 0; -} diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp new file mode 100644 index 0000000..e5319bb --- /dev/null +++ b/src/ld/ld.hpp @@ -0,0 +1,710 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __LD_HPP__ +#define __LD_HPP__ + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <assert.h> + +#include <vector> +#include <set> + + +namespace ld { + +// +// ld::File +// +// Abstract base class for all object or library files the linker processes. +// +// forEachAtom() iterates over the Atoms in the order they occur in the file. +// +// justInTimeforEachAtom(name) iterates over lazily created Atoms. For instance if +// File is a static library, justInTimeforEachAtom() will iterate over the base set +// of Atoms from the archive member implementing 'name'. +// +class File +{ +public: + enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease, objcConstraintRetainReleaseOrGC, objcConstraintGC }; + + class AtomHandler { + public: + virtual ~AtomHandler() {} + virtual void doAtom(const class Atom&) = 0; + virtual void doFile(const class File&) = 0; + }; + + File(const char* pth, time_t modTime, uint32_t ord) + : _path(pth), _modTime(modTime), _ordinal(ord) { } + virtual ~File() {} + const char* path() const { return _path; } + time_t modificationTime() const{ return _modTime; } + uint32_t ordinal() const { return _ordinal; } + virtual bool forEachAtom(AtomHandler&) const = 0; + virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const = 0; + virtual ObjcConstraint objCConstraint() const { return objcConstraintNone; } + virtual uint32_t cpuSubType() const { return 0; } + virtual uint32_t subFileCount() const { return 1; } +private: + const char* _path; + time_t _modTime; + uint32_t _ordinal; +}; + + +// +// minumum OS versions +// +enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, + mac10_6=0x000A0600, mac10_7=0x000A0700 }; +enum IPhoneVersionMin { iPhoneVersionUnset=0, iPhone2_0=0x00020000, iPhone3_1=0x00030100, + iPhone4_2=0x00040200, iPhone4_3=0x00040300 }; + +namespace relocatable { + // + // ld::relocatable::File + // + // Abstract base class for object files the linker processes. + // + // objcReplacementClasses() is reflects if the file was compiled for fix-and-continue + // + // debugInfo() returns if the object file contains debugger information (stabs or dwarf). + // + // stabs() lazily creates a vector of Stab objects for each atom + // + // canScatterAtoms() true for all compiler generated code. Hand written assembly can opt-in + // via .subsections_via_symbols directive. When true it means the linker can break up section + // content at symbol boundaries and do optimizations like coalescing, dead code stripping, or + // apply order files. + // + // optimize() used by libLTO to lazily generate code from llvm bit-code files + // + class File : public ld::File + { + public: + enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; + struct Stab { + const class Atom* atom; + uint8_t type; + uint8_t other; + uint16_t desc; + uint32_t value; + const char* string; + }; + + File(const char* pth, time_t modTime, uint32_t ord) + : ld::File(pth, modTime, ord) { } + virtual ~File() {} + virtual bool objcReplacementClasses() const = 0; + virtual DebugInfoKind debugInfo() const = 0; + virtual const char* debugInfoPath() const { return path(); } + virtual time_t debugInfoModificationTime() const { return modificationTime(); } + virtual const std::vector<Stab>* stabs() const = 0; + virtual bool canScatterAtoms() const = 0; + virtual bool hasLongBranchStubs() { return false; } + }; +} // namespace relocatable + + +namespace dylib { + + // + // ld::dylib::File + // + // Abstract base class for dynamic shared libraries read by the linker processes. + // + class File : public ld::File + { + public: + class DylibHandler + { + public: + virtual ~DylibHandler() {} + virtual File* findDylib(const char* installPath, const char* fromPath) = 0; + }; + + File(const char* pth, time_t modTime, uint32_t ord) + : ld::File(pth, modTime, ord), _dylibInstallPath(NULL), + _dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0), + _explicitlyLinked(false), _implicitlyLinked(false), + _lazyLoadedDylib(false), _weakLinked(false), _reExported(false), + _upward(false), _hasNonWeakImportedSymbols(false), + _hasWeakImportedSymbols(false), _dead(false) { } + const char* installPath() const { return _dylibInstallPath; } + uint32_t timestamp() const { return _dylibTimeStamp; } + uint32_t currentVersion() const { return _dylibCurrentVersion; } + uint32_t compatibilityVersion() const{ return _dylibCompatibilityVersion; } + void setExplicitlyLinked() { _explicitlyLinked = true; } + bool explicitlyLinked() const { return _explicitlyLinked; } + void setImplicitlyLinked() { _implicitlyLinked = true; } + bool implicitlyLinked() const { return _implicitlyLinked; } + // attributes of how dylib will be used when linked + void setWillBeLazyLoadedDylb() { _lazyLoadedDylib = true; } + bool willBeLazyLoadedDylib() const { return _lazyLoadedDylib; } + void setWillBeWeakLinked() { _weakLinked = true; } + bool willBeWeakLinked() const { return _weakLinked || + (_hasWeakImportedSymbols && !_hasNonWeakImportedSymbols); } + void setWillBeReExported() { _reExported = true; } + bool willBeReExported() const { return _reExported; } + void setWillBeUpwardDylib() { _upward = true; } + bool willBeUpwardDylib() const { return _upward; } + void setUsingNonWeakImportedSymbols(){ _hasNonWeakImportedSymbols = true; } + void setUsingWeakImportedSymbols() { _hasWeakImportedSymbols = true; } + void setWillBeRemoved(bool value) { _dead = value; } + bool willRemoved() const { return _dead; } + + virtual void processIndirectLibraries(DylibHandler* handler, bool addImplicitDylibs) = 0; + virtual bool providedExportAtom() const = 0; + virtual const char* parentUmbrella() const = 0; + virtual const std::vector<const char*>* allowableClients() const = 0; + virtual bool hasWeakExternals() const = 0; + virtual bool deadStrippable() const = 0; + virtual bool hasWeakDefinition(const char* name) const = 0; + virtual bool hasPublicInstallName() const = 0; + protected: + const char* _dylibInstallPath; + uint32_t _dylibTimeStamp; + uint32_t _dylibCurrentVersion; + uint32_t _dylibCompatibilityVersion; + bool _explicitlyLinked; + bool _implicitlyLinked; + bool _lazyLoadedDylib; + bool _weakLinked; + bool _reExported; + bool _upward; + bool _hasNonWeakImportedSymbols; + bool _hasWeakImportedSymbols; + bool _dead; + }; +} // namespace dylib + + + +// +// ld::Section +// +class Section +{ +public: + enum Type { typeUnclassified, typeCode, typePageZero, typeImportProxies, typeLinkEdit, typeMachHeader, typeStack, + typeLiteral4, typeLiteral8, typeLiteral16, typeConstants, typeTempLTO, + typeCString, typeNonStdCString, typeCStringPointer, typeUTF16Strings, typeCFString, typeObjC1Classes, + typeCFI, typeLSDA, typeDtraceDOF, typeUnwindInfo, typeObjCClassRefs, typeObjC2CategoryList, + typeZeroFill, typeTentativeDefs, typeLazyPointer, typeStub, typeNonLazyPointer, typeDyldInfo, + typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, + typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols, + typeTLVDefs, typeTLVZeroFill, typeTLVInitialValues, typeTLVInitializerPointers, typeTLVPointers, + typeFirstSection, typeLastSection }; + + + Section(const char* sgName, const char* sctName, + Type t, bool hidden=false) + : _segmentName(sgName), _sectionName(sctName), + _type(t), _hidden(hidden) {} + Section(const Section& sect) + : _segmentName(sect.segmentName()), _sectionName(sect.sectionName()), + _type(sect.type()), _hidden(sect.isSectionHidden()) {} + + bool operator==(const Section& rhs) const { return ( (_hidden==rhs._hidden) && + (strcmp(_segmentName, rhs._segmentName)==0) && + (strcmp(_sectionName, rhs._sectionName)==0) ); } + bool operator!=(const Section& rhs) const { return ! (*this == rhs); } + const char* segmentName() const { return _segmentName; } + const char* sectionName() const { return _sectionName; } + Type type() const { return _type; } + bool isSectionHidden() const { return _hidden; } + +private: + const char* _segmentName; + const char* _sectionName; + Type _type; + bool _hidden; +}; + + + +// +// ld::Fixup +// +// A Fixup describes how part of an Atom's content must be fixed up. For instance, +// an instruction may contain a displacement to another Atom that must be +// fixed up by the linker. +// +// A Fixup my reference another Atom. There are two kinds of references: direct and by-name. +// With a direct reference, the target is bound by the File that created it. +// For instance a reference to a static would produce a direct reference. +// A by-name reference requires the linker to find the target Atom with the +// required name in order to be bound. +// +// For a link to succeed all Fixup must be bound. +// +// A Reference also has a fix-up-offset. This is the offset into the content of the +// Atom holding the reference where the fix-up (relocation) will be applied. +// +// +struct Fixup +{ + enum TargetBinding { bindingNone, bindingByNameUnbound, bindingDirectlyBound, bindingByContentBound, bindingsIndirectlyBound }; + enum Cluster { k1of1, k1of2, k2of2, k1of3, k2of3, k3of3, k1of4, k2of4, k3of4, k4of4, k1of5, k2of5, k3of5, k4of5, k5of5 }; + enum Kind { kindNone, kindNoneFollowOn, + // grouping + kindNoneGroupSubordinate, + kindNoneGroupSubordinateFDE, kindNoneGroupSubordinateLSDA, kindNoneGroupSubordinatePersonality, + // value calculations + kindSetTargetAddress, + kindSubtractTargetAddress, + kindAddAddend, + kindSubtractAddend, + kindSetTargetImageOffset, + kindSetTargetSectionOffset, + kindSetTargetTLVTemplateOffset, + // pointer store kinds (of current calculated value) + kindStore8, + kindStoreLittleEndian16, + kindStoreLittleEndianLow24of32, + kindStoreLittleEndian32, + kindStoreLittleEndian64, + kindStoreBigEndian16, + kindStoreBigEndianLow24of32, + kindStoreBigEndian32, + kindStoreBigEndian64, + // Intel specific store kinds + kindStoreX86BranchPCRel8, kindStoreX86BranchPCRel32, + kindStoreX86PCRel8, kindStoreX86PCRel16, + kindStoreX86PCRel32, kindStoreX86PCRel32_1, kindStoreX86PCRel32_2, kindStoreX86PCRel32_4, + kindStoreX86PCRel32GOTLoad, kindStoreX86PCRel32GOTLoadNowLEA, kindStoreX86PCRel32GOT, + kindStoreX86PCRel32TLVLoad, kindStoreX86PCRel32TLVLoadNowLEA, + kindStoreX86Abs32TLVLoad, kindStoreX86Abs32TLVLoadNowLEA, + // ARM specific store kinds + kindStoreARMBranch24, kindStoreThumbBranch22, + kindStoreARMLoad12, + kindStoreARMLow16, kindStoreARMHigh16, + kindStoreThumbLow16, kindStoreThumbHigh16, + // PowerPC specific store kinds + kindStorePPCBranch24, kindStorePPCBranch14, + kindStorePPCPicLow14, kindStorePPCPicLow16, kindStorePPCPicHigh16AddLow, + kindStorePPCAbsLow14, kindStorePPCAbsLow16, kindStorePPCAbsHigh16AddLow, kindStorePPCAbsHigh16, + // dtrace probes + kindDtraceExtra, + kindStoreX86DtraceCallSiteNop, kindStoreX86DtraceIsEnableSiteClear, + kindStoreARMDtraceCallSiteNop, kindStoreARMDtraceIsEnableSiteClear, + kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear, + kindStorePPCDtraceCallSiteNop, kindStorePPCDtraceIsEnableSiteClear, + // lazy binding + kindLazyTarget, kindSetLazyOffset, + // pointer store combinations + kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32 + kindStoreTargetAddressLittleEndian64, // kindSetTargetAddress + kindStoreLittleEndian64 + kindStoreTargetAddressBigEndian32, // kindSetTargetAddress + kindStoreBigEndian32 + kindStoreTargetAddressBigEndian64, // kindSetTargetAddress + kindStoreBigEndian364 + kindSetTargetTLVTemplateOffsetLittleEndian32, // kindSetTargetTLVTemplateOffset + kindStoreLittleEndian32 + kindSetTargetTLVTemplateOffsetLittleEndian64, // kindSetTargetTLVTemplateOffset + kindStoreLittleEndian64 + // Intel value calculation and store combinations + kindStoreTargetAddressX86PCRel32, // kindSetTargetAddress + kindStoreX86PCRel32 + kindStoreTargetAddressX86BranchPCRel32, // kindSetTargetAddress + kindStoreX86BranchPCRel32 + kindStoreTargetAddressX86PCRel32GOTLoad,// kindSetTargetAddress + kindStoreX86PCRel32GOTLoad + kindStoreTargetAddressX86PCRel32GOTLoadNowLEA,// kindSetTargetAddress + kindStoreX86PCRel32GOTLoadNowLEA + kindStoreTargetAddressX86PCRel32TLVLoad, // kindSetTargetAddress + kindStoreX86PCRel32TLVLoad + kindStoreTargetAddressX86PCRel32TLVLoadNowLEA, // kindSetTargetAddress + kindStoreX86PCRel32TLVLoadNowLEA + kindStoreTargetAddressX86Abs32TLVLoad, // kindSetTargetAddress + kindStoreX86Abs32TLVLoad + kindStoreTargetAddressX86Abs32TLVLoadNowLEA, // kindSetTargetAddress + kindStoreX86Abs32TLVLoadNowLEA + // ARM value calculation and store combinations + kindStoreTargetAddressARMBranch24, // kindSetTargetAddress + kindStoreARMBranch24 + kindStoreTargetAddressThumbBranch22, // kindSetTargetAddress + kindStoreThumbBranch22 + kindStoreTargetAddressARMLoad12, // kindSetTargetAddress + kindStoreARMLoad12 + // PowerPC value calculation and store combinations + kindStoreTargetAddressPPCBranch24, // kindSetTargetAddress + kindStorePPCBranch24 + }; + + union { + const Atom* target; + const char* name; + uint64_t addend; + uint32_t bindingIndex; + } u; + uint32_t offsetInAtom; + Kind kind : 8; + Cluster clusterSize : 4; + bool weakImport : 1; + TargetBinding binding : 3; + bool contentAddendOnly : 1; + bool contentDetlaToAddendOnly : 1; + + typedef Fixup* iterator; + + Fixup() : + offsetInAtom(0), kind(kindNone), clusterSize(k1of1), weakImport(false), + binding(bindingNone), + contentAddendOnly(false), contentDetlaToAddendOnly(false) { u.target = NULL; } + + Fixup(Kind k, Atom* targetAtom) : + offsetInAtom(0), kind(k), clusterSize(k1of1), weakImport(false), + binding(Fixup::bindingDirectlyBound), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(targetAtom != NULL); u.target = targetAtom; } + + Fixup(uint32_t off, Cluster c, Kind k) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), + binding(Fixup::bindingNone), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { u.addend = 0; } + + Fixup(uint32_t off, Cluster c, Kind k, bool weakIm, const char* name) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(weakIm), + binding(Fixup::bindingByNameUnbound), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(name != NULL); u.name = name; } + + Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const char* name) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(name != NULL); u.name = name; } + + Fixup(uint32_t off, Cluster c, Kind k, const Atom* targetAtom) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), + binding(Fixup::bindingDirectlyBound), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(targetAtom != NULL); u.target = targetAtom; } + + Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const Atom* targetAtom) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { assert(targetAtom != NULL); u.target = targetAtom; } + + Fixup(uint32_t off, Cluster c, Kind k, uint64_t addend) : + offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), + binding(Fixup::bindingNone), + contentAddendOnly(false), contentDetlaToAddendOnly(false) + { u.addend = addend; } + + bool firstInCluster() const { + switch (clusterSize) { + case k1of1: + case k1of2: + case k1of3: + case k1of4: + case k1of5: + return true; + default: + break; + } + return false; + } + + bool lastInCluster() const { + switch (clusterSize) { + case k1of1: + case k2of2: + case k3of3: + case k4of4: + case k5of5: + return true; + default: + break; + } + return false; + } + +}; + +// +// ld::Atom +// +// An atom is the fundamental unit of linking. A C function or global variable is an atom. +// An atom has content and attributes. The content of a function atom is the instructions +// that implement the function. The content of a global variable atom is its initial bits. +// +// Name: +// The name of an atom is the label name generated by the compiler. A C compiler names foo() +// as _foo. A C++ compiler names foo() as __Z3foov. +// The name refers to the first byte of the content. An atom cannot have multiple entry points. +// Such code is modeled as multiple atoms, each having a "follow on" reference to the next. +// A "follow on" reference is a contraint to the linker to the atoms must be laid out contiguously. +// +// Scope: +// An atom is in one of three scopes: translation-unit, linkage-unit, or global. These correspond +// to the C visibility of static, hidden, default. +// +// DefinitionKind: +// An atom is one of five defintion kinds: +// regular Most atoms. +// weak C++ compiler makes some functions weak if there might be multiple copies +// that the linker needs to coalesce. +// tentative A straggler from ancient C when the extern did not exist. "int foo;" is ambiguous. +// It could be a prototype or it could be a definition. +// external This is a "proxy" atom produced by a dylib reader. It has no content. It exists +// so that the graph of Atoms can be complete. +// external-weak Same as external, but the definition in the dylib is weak. +// +// SymbolTableInclusion: +// An atom may or may not be in the symbol table in an object file. +// in Most atoms for functions or global data +// not-in Anonymous atoms such literal c-strings, or other compiler generated data +// not-in-final Atom whose name should not be in the symbol table of final linkd image (e.g. 'l' labels .eh labels) +// in-never-strip Atom whose name the strip tool should never remove (e.g. REFERENCED_DYNAMICALLY in mach-o) +// +// ContentType: +// Some atoms require specially processing by the linker based on their content. For instance, zero-fill data +// atom are group together at the end of the DATA segment to reduce disk size. +// +// ObjectAddress: +// For reproducability, the linker lays out atoms in the order they occurred in the source (object) files. +// The objectAddress() method returns the address of an atom in the object file so that the linker +// can arrange the atoms. +// +// +class Atom +{ +public: + enum Scope { scopeTranslationUnit, scopeLinkageUnit, scopeGlobal }; + enum Definition { definitionRegular, definitionTentative, definitionAbsolute, definitionProxy }; + enum Combine { combineNever, combineByName, combineByNameAndContent, combineByNameAndReferences }; + enum ContentType { typeUnclassified, typeZeroFill, typeCString, typeCFI, typeLSDA, typeSectionStart, + typeSectionEnd, typeBranchIsland, typeLazyPointer, typeStub, typeNonLazyPointer, + typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, + typeLTOtemporary, typeResolver, + typeTLV, typeTLVZeroFill, typeTLVInitialValue, typeTLVInitializerPointers }; + + enum SymbolTableInclusion { symbolTableNotIn, symbolTableNotInFinalLinkedImages, symbolTableIn, + symbolTableInAndNeverStrip, symbolTableInAsAbsolute, + symbolTableInWithRandomAutoStripLabel }; + struct Alignment { + Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {} + uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); } + uint16_t powerOf2; + uint16_t modulus; + }; + struct LineInfo { + const char* fileName; + uint32_t atomOffset; + uint32_t lineNumber; + + typedef LineInfo* iterator; + }; + struct UnwindInfo { + uint32_t startOffset; + uint32_t unwindInfo; + + typedef UnwindInfo* iterator; + }; + + Atom(const Section& sect, Definition d, Combine c, Scope s, ContentType ct, + SymbolTableInclusion i, bool dds, bool thumb, bool al, Alignment a) : + _section(§), _address(0), _alignmentModulus(a.modulus), + _alignmentPowerOf2(a.powerOf2), _definition(d), _combine(c), + _dontDeadStrip(dds), _thumb(thumb), _alias(al), _autoHide(false), + _contentType(ct), _symbolTableInclusion(i), + _scope(s), _mode(modeSectionOffset), + _overridesADylibsWeakDef(false), _coalescedAway(false), + _weakImport(false), _live(false), _machoSection(0) + { + #ifndef NDEBUG + switch ( _combine ) { + case combineByNameAndContent: + case combineByNameAndReferences: + assert(_symbolTableInclusion == symbolTableNotIn); + assert(_scope != scopeGlobal); + break; + case combineByName: + case combineNever: + break; + }; + #endif + } + virtual ~Atom() {} + + const Section& section() const { return *_section; } + Definition definition() const { return _definition; } + Combine combine() const { return _combine; } + Scope scope() const { return _scope; } + ContentType contentType() const { return _contentType; } + SymbolTableInclusion symbolTableInclusion() const{ return _symbolTableInclusion; } + bool dontDeadStrip() const { return _dontDeadStrip; } + bool isThumb() const { return _thumb; } + bool isAlias() const { return _alias; } + Alignment alignment() const { return Alignment(_alignmentPowerOf2, _alignmentModulus); } + bool overridesDylibsWeakDef() const { return _overridesADylibsWeakDef; } + bool coalescedAway() const { return _coalescedAway; } + bool weakImported() const { return _weakImport; } + bool autoHide() const { return _autoHide; } + bool live() const { return _live; } + uint8_t machoSection() const { assert(_machoSection != 0); return _machoSection; } + + void setScope(Scope s) { _scope = s; } + void setSymbolTableInclusion(SymbolTableInclusion i) + { _symbolTableInclusion = i; } + void setCombine(Combine c) { _combine = c; } + void setOverridesDylibsWeakDef() { _overridesADylibsWeakDef = true; } + void setCoalescedAway() { _coalescedAway = true; } + void setWeakImported() { _weakImport = true; assert(_definition == definitionProxy); } + void setAutoHide() { _autoHide = true; } + void setLive() { _live = true; } + void setLive(bool value) { _live = value; } + void setMachoSection(unsigned x) { assert(x != 0); assert(x < 256); _machoSection = x; } + void setSectionOffset(uint64_t o){ assert(_mode == modeSectionOffset); _address = o; _mode = modeSectionOffset; } + void setSectionStartAddress(uint64_t a) { assert(_mode == modeSectionOffset); _address += a; _mode = modeFinalAddress; } + uint64_t sectionOffset() const { assert(_mode == modeSectionOffset); return _address; } + uint64_t finalAddress() const { assert(_mode == modeFinalAddress); return _address; } + + virtual const File* file() const = 0; + virtual bool translationUnitSource(const char** dir, const char** name) const = 0; + virtual const char* name() const = 0; + virtual uint64_t objectAddress() const = 0; + virtual uint64_t size() const = 0; + virtual void copyRawContent(uint8_t buffer[]) const = 0; + virtual const uint8_t* rawContentPointer() const { return NULL; } + virtual unsigned long contentHash(const class IndirectBindingTable&) const { return 0; } + virtual bool canCoalesceWith(const Atom& rhs, const class IndirectBindingTable&) const { return false; } + virtual Fixup::iterator fixupsBegin() const { return NULL; } + virtual Fixup::iterator fixupsEnd() const { return NULL; } + virtual UnwindInfo::iterator beginUnwind() const { return NULL; } + virtual UnwindInfo::iterator endUnwind() const { return NULL; } + virtual LineInfo::iterator beginLineInfo() const { return NULL; } + virtual LineInfo::iterator endLineInfo() const { return NULL; } + +protected: + enum AddressMode { modeSectionOffset, modeFinalAddress }; + + void setAttributesFromAtom(const Atom& a) { + _section = a._section; + _alignmentModulus = a._alignmentModulus; + _alignmentPowerOf2 = a._alignmentPowerOf2; + _definition = a._definition; + _combine = a._combine; + _dontDeadStrip = a._dontDeadStrip; + _thumb = a._thumb; + _alias = a._alias; + _autoHide = a._autoHide; + _contentType = a._contentType; + _symbolTableInclusion = a._symbolTableInclusion; + _scope = a._scope; + _mode = a._mode; + _overridesADylibsWeakDef = a._overridesADylibsWeakDef; + _coalescedAway = a._coalescedAway; + _weakImport = a._weakImport; + } + + const Section * _section; + uint64_t _address; + uint16_t _alignmentModulus; + uint8_t _alignmentPowerOf2; + Definition _definition : 2; + Combine _combine : 2; + bool _dontDeadStrip : 1; + bool _thumb : 1; + bool _alias : 1; + int _autoHide : 1; + ContentType _contentType : 5; + SymbolTableInclusion _symbolTableInclusion : 3; + Scope _scope : 2; + AddressMode _mode: 2; + bool _overridesADylibsWeakDef : 1; + bool _coalescedAway : 1; + bool _weakImport : 1; + bool _live : 1; + unsigned _machoSection : 8; +}; + + +class IndirectBindingTable +{ +public: + virtual const char* indirectName(uint32_t bindingIndex) const = 0; + virtual const ld::Atom* indirectAtom(uint32_t bindingIndex) const = 0; +}; + + +class Internal +{ +public: + class FinalSection : public ld::Section { + public: + FinalSection(const Section& sect) : Section(sect), address(0), + fileOffset(0), size(0), alignment(0), + indirectSymTabStartIndex(0), indirectSymTabElementSize(0), + relocStart(0), relocCount(0), + hasLocalRelocs(false), hasExternalRelocs(false) {} + std::vector<const Atom*> atoms; + uint64_t address; + uint64_t fileOffset; + uint64_t size; + uint32_t alignmentPaddingBytes; + uint8_t alignment; + uint32_t indirectSymTabStartIndex; + uint32_t indirectSymTabElementSize; + uint32_t relocStart; + uint32_t relocCount; + bool hasLocalRelocs; + bool hasExternalRelocs; + }; + + virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0; + virtual ld::Internal::FinalSection* getFinalSection(const ld::Section& inputSection) = 0; + virtual ~Internal() {} + Internal() : bundleLoader(NULL), + entryPoint(NULL), classicBindingHelper(NULL), + lazyBindingHelper(NULL), compressedFastBinderProxy(NULL), + objcObjectConstraint(ld::File::objcConstraintNone), + objcDylibConstraint(ld::File::objcConstraintNone), + cpuSubType(0), + allObjectFilesScatterable(true), hasObjcReplacementClasses(false), + someObjectFileHasDwarf(false), usingHugeSections(false) { } + + std::vector<FinalSection*> sections; + std::vector<ld::dylib::File*> dylibs; + std::vector<ld::relocatable::File::Stab> stabs; + std::vector<const ld::Atom*> indirectBindingTable; + const ld::dylib::File* bundleLoader; + const Atom* entryPoint; + const Atom* classicBindingHelper; + const Atom* lazyBindingHelper; + const Atom* compressedFastBinderProxy; + ld::File::ObjcConstraint objcObjectConstraint; + ld::File::ObjcConstraint objcDylibConstraint; + uint32_t cpuSubType; + bool allObjectFilesScatterable; + bool hasObjcReplacementClasses; + bool someObjectFileHasDwarf; + bool usingHugeSections; +}; + + + + + + + + + + + + +} // namespace ld + +#endif // __LD_HPP__ diff --git a/src/ld/lto_file.hpp b/src/ld/lto_file.hpp new file mode 100644 index 0000000..24d3f58 --- /dev/null +++ b/src/ld/lto_file.hpp @@ -0,0 +1,642 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LTO_READER_H__ +#define __LTO_READER_H__ + +#include <stdlib.h> +#include <mach-o/dyld.h> +#include <vector> +#include <ext/hash_set> +#include <ext/hash_map> + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ld.hpp" + +#include "llvm-c/lto.h" + + +namespace lto { + + +// +// ld64 only tracks non-internal symbols from an llvm bitcode file. +// We model this by having an InternalAtom which represent all internal functions and data. +// All non-interal symbols from a bitcode file are represented by an Atom +// and each Atom has a reference to the InternalAtom. The InternalAtom +// also has references to each symbol external to the bitcode file. +// +class InternalAtom : public ld::Atom +{ +public: + InternalAtom(class File& f); + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "import-atom"; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() { return &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() { return &_undefs[_undefs.size()]; } + + // for adding references to symbols outside bitcode file + void addReference(const char* name) + { _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, + ld::Fixup::fixupNone, false, name)); } +private: + + ld::File& _file; + std::vector<ld::Fixup> _undefs; +}; + + +// +// LLVM bitcode file +// +class File : public ld::relocatable::File +{ +public: + File(const char* path, time_t mTime, const uint8_t* content, + uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); + virtual ~File(); + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&); + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) + { return false; } + + // overrides of ld::relocatable::File + virtual bool objcReplacementClasses() { return false; } + virtual DebugInfoKind debugInfo() { return ld::relocatable::File::kDebugInfoNone; } + virtual std::vector<ld::relocatable::File::Stab>* stabs() { return NULL; } + virtual bool canScatterAtoms() { return true; } + + lto_module_t module() { return _module; } + class InternalAtom& internalAtom() { return _internalAtom; } +private: + friend class Atom; + friend class InternalAtom; + + cpu_type_t _architecture; + class InternalAtom _internalAtom; + class Atom* _atomArray; + uint32_t _atomArrayCount; + lto_module_t _module; + ld::Section _section; +}; + +// +// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, +// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After +// optimization is performed, real Atoms are created for these symobls. However these real Atoms +// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate +// methods to real atom. +// +class Atom : public ld::Atom +{ +public: + Atom(File& f, const char* name, ld::Atom::Scope s, + ld::Atom::Definition d, ld::Atom::Alignment a); + + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } + virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } + virtual void copyRawContent(uint8_t buffer[]) const + { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); } + + ld::Atom* compiledAtom() { return _compiledAtom; } + void setCompiledAtom(ld::Atom& atom) + { _compiledAtom = &atom; } +private: + + File& _file; + const char* _name; + ld::Atom* _compiledAtom; +}; + + + + + + + +class Parser +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); + static const char* fileKind(const uint8_t* fileContent); + static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, uint32_t ordinal, cpu_type_t architecture); + static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } + static bool optimize(const std::vector<ld::Atom*>& allAtoms, std::vector<ld::Atom*>& newAtoms, + std::vector<const char*>& additionalUndefines, + const std::set<ld::Atom*>&, + std::vector<ld::Atom*>& newDeadAtoms, + uint32_t nextInputOrdinal, + ld::OutFile* writer, ld::Atom* entryPointAtom, + const std::vector<const char*>& llvmOptions, + bool allGlobalsAReDeadStripRoots, + bool verbose, bool saveTemps, + const char* outputFilePath, + bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, + bool allowTextRelocs, cpu_type_t arch); + + static const char* ltoVersion() { return ::lto_get_version(); } + +private: + static const char* tripletPrefixForArch(cpu_type_t arch); + static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch); + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom; + + class AtomSyncer : public ld::File::AtomHandler { + public: + AtomSyncer(std::vector<const char*>& a, std::vector<ld::Atom*>&na, + CStringToAtom la, CStringToAtom dla) : + additionalUndefines(a), newAtoms(na), llvmAtoms(la), deadllvmAtoms(dla) { } + virtual void doAtom(class ld::Atom&); + + std::vector<const char*>& additionalUndefines; + std::vector<ld::Atom*>& newAtoms; + CStringToAtom llvmAtoms; + CStringToAtom deadllvmAtoms; + }; + + static std::vector<File*> _s_files; +}; + +std::vector<File*> Parser::_s_files; + + +const char* Parser::tripletPrefixForArch(cpu_type_t arch) +{ + switch (arch) { + case CPU_TYPE_POWERPC: + return "powerpc-"; + case CPU_TYPE_POWERPC64: + return "powerpc64-"; + case CPU_TYPE_I386: + return "i386-"; + case CPU_TYPE_X86_64: + return "x86_64-"; + case CPU_TYPE_ARM: + return "arm"; + } + return ""; +} + +bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) +{ + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); +} + +const char* Parser::fileKind(const uint8_t* p) +{ + if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { + uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + switch (arch) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + case CPU_TYPE_ARM: + return "arm"; + } + return "unknown bitcode architecture"; + } + return NULL; +} + +File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + uint32_t ordinal, cpu_type_t architecture) +{ + File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); + _s_files.push_back(f); + return f; +} + + +ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch) +{ + switch ( arch ) { + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Parser<ppc>::validFile(p) ) + return mach_o::relocatable::Parser<ppc>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Parser<ppc64>::validFile(p) ) + return mach_o::relocatable::Parser<ppc64>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Parser<x86>::validFile(p) ) + return mach_o::relocatable::Parser<x86>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Parser<x86_64>::validFile(p) ) + return mach_o::relocatable::Parser<x86_64>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Parser<arm>::validFile(p) ) + return mach_o::relocatable::Parser<arm>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); + break; + } + throw "LLVM LTO, file is not of required architecture"; +} + + + +File::File(const char* path, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ordinal, cpu_type_t arch) + : ld::relocatable::File(path,mTime,ordinal), _architecture(arch), _internalAtom(*this), + _atomArray(NULL), _atomArrayCount(0), _module(NULL), + _section("__TEXT_", "__tmp_lto", ld::Section::typeUnclassified) +{ + // create llvm module + _module = ::lto_module_create_from_memory(content, contentLength); + if ( _module == NULL ) + throwf("could not parse object file %s: %s", path, lto_get_error_message()); + + // create atom for each global symbol in module + uint32_t count = ::lto_module_get_num_symbols(_module); + _atomArray = (Atom*)malloc(sizeof(Atom)*count); + for (uint32_t i=0; i < count; ++i) { + const char* name = ::lto_module_get_symbol_name(_module, i); + lto_symbol_attributes attr = lto_module_get_symbol_attribute(_module, i); + + // <rdar://problem/6378110> LTO doesn't like dtrace symbols + // ignore dtrace static probes for now + // later when codegen is done and a mach-o file is produces the probes will be processed + if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) + continue; + + ld::Atom::Definition def; + switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { + case LTO_SYMBOL_DEFINITION_REGULAR: + def = ld::Atom::definitionRegular; + break; + case LTO_SYMBOL_DEFINITION_TENTATIVE: + def = ld::Atom::definitionTentative; + break; + case LTO_SYMBOL_DEFINITION_WEAK: + def = ld::Atom::definitionRegular; + break; + case LTO_SYMBOL_DEFINITION_UNDEFINED: + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: + def = ld::Atom::definitionProxy; + break; + default: + throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); + } + + // make LLVM atoms for definitions and a reference for undefines + if ( def != ld::Atom::definitionProxy ) { + ld::Atom::Scope scope; + switch ( attr & LTO_SYMBOL_SCOPE_MASK) { + case LTO_SYMBOL_SCOPE_INTERNAL: + scope = ld::Atom::scopeTranslationUnit; + break; + case LTO_SYMBOL_SCOPE_HIDDEN: + scope = ld::Atom::scopeLinkageUnit; + break; + case LTO_SYMBOL_SCOPE_DEFAULT: + scope = ld::Atom::scopeGlobal; + break; + default: + throwf("unknown scope for symbol %s in bitcode file %s", name, path); + } + // only make atoms for non-internal symbols + if ( scope == ld::Atom::scopeTranslationUnit ) + continue; + uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); + // make Atom using placement new operator + new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, alignment); + } + else { + // add to list of external references + _internalAtom.addReference(name); + } + } +} + +File::~File() +{ + if ( _module != NULL ) + ::lto_module_dispose(_module); +} + +bool File::forEachAtom(ld::File::AtomHandler& handler) +{ + handler.doAtom(_internalAtom); + for(uint32_t i=0; i < _atomArrayCount; ++i) { + handler.doAtom(_atomArray[i]); + } + return true; +} + +InternalAtom::InternalAtom(File& f) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeLTOtemporary, ld::Atom::symbolTableNotIn, false, false, ld::Atom::Alignment(0)), + _file(f) +{ +} + +Atom::Atom(File& f, const char* name, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Alignment a) + : ld::Atom(f._section, d, ld::Atom::combineNever, s, ld::Atom::typeLTOtemporary, ld::Atom::symbolTableIn, false, false, a), + _file(f), _name(name), _compiledAtom(NULL) +{ +} + + + + +bool Parser::optimize(const std::vector<ld::Atom*>& allAtoms, std::vector<ld::Atom*>& newAtoms, + std::vector<const char*>& additionalUndefines, + const std::set<ld::Atom*>& deadAtoms, + std::vector<ld::Atom*>& newlyDeadAtoms, + uint32_t nextInputOrdinal, + ld::OutFile* writer, ld::Atom* entryPointAtom, + const std::vector<const char*>& llvmOptions, + bool allGlobalsAReDeadStripRoots, + bool verbose, bool saveTemps, + const char* outputFilePath, + bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, + bool allowTextRelocs, cpu_type_t arch) +{ + // exit quickly if nothing to do + if ( _s_files.size() == 0 ) + return false; + + // print out LTO version string if -v was used + if ( verbose ) + fprintf(stderr, "%s\n", lto_get_version()); + + // create optimizer and add each Reader + lto_code_gen_t generator = ::lto_codegen_create(); + for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + if ( ::lto_codegen_add_module(generator, (*it)->module()) ) + throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); + } + + // add any -mllvm command line options + for (std::vector<const char*>::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { + ::lto_codegen_debug_options(generator, *it); + } + + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also + // defined in llvm bitcode file. + CStringSet nonLLVMRefs; + CStringToAtom llvmAtoms; + bool hasNonllvmAtoms = false; + for (std::vector<ld::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + ld::Atom* atom = *it; + // only look at references that come from an atom that is not an llvm atom + if ( atom->contentType() != ld::Atom::typeLTOtemporary ) { + // remember if we've seen any atoms not from an llvm reader and not from the writer +// if ( atom->getFile() != writer ) +// hasNonllvmAtoms = true; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->binding != ld::Fixup::bindingByNameBound ) + continue; + // and reference an llvm atom + if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(fit->u.target->name()); + } + } + else { + llvmAtoms[atom->name()] = (Atom*)atom; + } + } + // if entry point is in a llvm bitcode file, it must be preserved by LTO + if ( entryPointAtom != NULL ) { + if ( entryPointAtom->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(entryPointAtom->name()); + } + + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions + // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced + // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead + // atom so that the linker can replace it with the mach-o one later. + CStringToAtom deadllvmAtoms; + for (std::set<ld::Atom*>::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { + ld::Atom* atom = *it; + if ( atom->contentType() == ld::Atom::typeLTOtemporary ) { + const char* name = atom->name(); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)atom; + } + } + + + // tell code generator about symbols that must be preserved + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + Atom* atom = it->second; + // Include llvm Symbol in export list if it meets one of following two conditions + // 1 - atom scope is global (and not linkage unit). + // 2 - included in nonLLVMRefs set. + // If a symbol is not listed in exportList then LTO is free to optimize it away. + if ( (atom->scope() == ld::Atom::scopeGlobal) ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + + // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) + if ( relocatable && !hasNonllvmAtoms ) { + if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { + // HACK, no good way to tell linker we are all done, so just quit + exit(0); + } + warning("could not produce merged bitcode file"); + } + + // set code-gen model + lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + if ( mainExecutable ) { + if ( staticExecutable ) { + // darwin x86_64 "static" code model is really dynamic code model + if ( arch == CPU_TYPE_X86_64 ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_STATIC; + } + else { + if ( pie ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + } + } + else { + if ( allowTextRelocs ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + } + if ( ::lto_codegen_set_pic_model(generator, model) ) + throwf("could not create set codegen model: %s", lto_get_error_message()); + + // if requested, save off merged bitcode file + if ( saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } + +#if LTO_API_VERSION >= 3 + // find assembler next to linker + char path[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { + char* lastSlash = strrchr(path, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, "as"); + struct stat statInfo; + if ( stat(path, &statInfo) == 0 ) + ::lto_codegen_set_assembler_path(generator, path); + } + } +#endif + // run code generator + size_t machOFileLen; + const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + + // if requested, save off temp mach-o file + if ( saveTemps ) { + char tempMachoPath[MAXPATHLEN]; + strcpy(tempMachoPath, outputFilePath); + strcat(tempMachoPath, ".lto.o"); + int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + } + + // parse generated mach-o file into a MachOReader + ld::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, arch); + + // sync generated mach-o atoms with existing atoms ld knows about + AtomSyncer syncer(additionalUndefines,newAtoms,llvmAtoms,deadllvmAtoms); + machoFile->forEachAtom(syncer); + + // Remove InternalAtoms from ld + for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + newlyDeadAtoms.push_back(&((*it)->internalAtom())); + } + // Remove Atoms from ld if code generator optimized them away + for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { + // check if setRealAtom() called on this Atom + if ( li->second->compiledAtom() == NULL ) + newlyDeadAtoms.push_back(li->second); + } + + return true; +} + + +void Parser::AtomSyncer::doAtom(ld::Atom& machoAtom) +{ + // update proxy atoms to point to real atoms and find new atoms + const char* name = machoAtom.name(); + if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { + CStringToAtom::iterator pos = llvmAtoms.find(name); + if ( pos != llvmAtoms.end() ) { + // turn Atom into a proxy for this mach-o atom + pos->second->setCompiledAtom(machoAtom); + } + else { + // an atom of this name was not in the allAtoms list the linker gave us + if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { + // this corresponding to an atom that the linker coalesced away. + // Don't pass it back as a new atom + } + else + { + // this is something new that lto conjured up, tell ld its new + newAtoms.push_back(&machoAtom); + } + } + } + else { + // ld only knew about non-satic atoms, so this one must be new + newAtoms.push_back(&machoAtom); + } + + // adjust fixups to go through proxy atoms + for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + break; + case ld::Fixup::bindingByNameUnbound: + // don't know if this target has been seen by linker before or if it is new + // be conservitive and tell linker it is new + additionalUndefines.push_back(fit->u.name); + break; + case ld::Fixup::bindingByNameBound: + break; + case ld::Fixup::bindingDirectlyBound: + // If mach-o atom is referencing another mach-o atom then + // reference is not going through Atom proxy. Fix it here to ensure that all + // llvm symbol references always go through Atom proxy. + break; + case ld::Fixup::bindingByContentBound: + break; + } + } + +} + + + +}; // namespace lto + + +#endif + diff --git a/src/ld/parsers/archive_file.cpp b/src/ld/parsers/archive_file.cpp new file mode 100644 index 0000000..8c866cd --- /dev/null +++ b/src/ld/parsers/archive_file.cpp @@ -0,0 +1,522 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <sys/param.h> +#include <mach-o/ranlib.h> +#include <ar.h> + +#include <vector> +#include <set> +#include <algorithm> +#include <ext/hash_map> + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + +#include "macho_relocatable_file.h" +#include "lto_file.h" +#include "archive_file.h" + + +namespace archive { + +typedef const struct ranlib* ConstRanLibPtr; + +// forward reference +template <typename A> class File; + + +template <typename A> +class Parser +{ +public: + typedef typename A::P P; + + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts) { + return File<A>::validFile(fileContent, fileLength, opts); } + static File<A>* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t mTime, + uint32_t ordinal, const ParserOptions& opts) { + return new File<A>(fileContent, fileLength, path, mTime, + ordinal, opts); + } + +}; + +template <typename A> +class File : public ld::File +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts); + File(const uint8_t* fileContent, uint64_t fileLength, + const char* pth, time_t modTime, + uint32_t ord, const ParserOptions& opts); + virtual ~File() {} + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; + virtual uint32_t subFileCount() const { return _archiveFilelength/sizeof(ar_hdr); } + +private: + static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts); + static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength, + const mach_o::relocatable::ParserOptions& opts); + static cpu_type_t architecture(); + + + class Entry : ar_hdr + { + public: + const char* name() const; + time_t modificationTime() const; + const uint8_t* content() const; + uint32_t contentSize() const; + const Entry* next() const; + private: + bool hasLongName() const; + unsigned int getLongNameSpace() const; + + }; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap; + + typedef typename A::P P; + typedef typename A::P::E E; + + const struct ranlib* ranlibHashSearch(const char* name) const; + ld::relocatable::File* makeObjectFileForMember(const Entry* member) const; + bool memberHasObjCCategories(const Entry* member) const; + void dumpTableOfContents(); + void buildHashTable(); + + const uint8_t* _archiveFileContent; + uint64_t _archiveFilelength; + const struct ranlib* _tableOfContents; + uint32_t _tableOfContentCount; + const char* _tableOfContentStrings; + mutable std::vector<ld::relocatable::File*> _instantiatedFiles; + mutable std::set<const class Entry*> _instantiatedEntries; + NameToEntryMap _hashTable; + const bool _forceLoadAll; + const bool _forceLoadObjC; + const bool _forceLoadThis; + const bool _verboseLoad; + const bool _logAllFiles; + const mach_o::relocatable::ParserOptions _objOpts; +}; + + +template <typename A> +bool File<A>::Entry::hasLongName() const +{ + return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); +} + +template <typename A> +unsigned int File<A>::Entry::getLongNameSpace() const +{ + char* endptr; + long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); + return result; +} + +template <typename A> +const char* File<A>::Entry::name() const +{ + if ( this->hasLongName() ) { + int len = this->getLongNameSpace(); + static char longName[256]; + strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); + longName[len] = '\0'; + return longName; + } + else { + static char shortName[20]; + strncpy(shortName, this->ar_name, 16); + shortName[16] = '\0'; + char* space = strchr(shortName, ' '); + if ( space != NULL ) + *space = '\0'; + return shortName; + } +} + +template <typename A> +time_t File<A>::Entry::modificationTime() const +{ + char temp[14]; + strncpy(temp, this->ar_date, 12); + temp[12] = '\0'; + char* endptr; + return (time_t)strtol(temp, &endptr, 10); +} + + +template <typename A> +const uint8_t* File<A>::Entry::content() const +{ + if ( this->hasLongName() ) + return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); + else + return ((uint8_t*)this) + sizeof(ar_hdr); +} + + +template <typename A> +uint32_t File<A>::Entry::contentSize() const +{ + char temp[12]; + strncpy(temp, this->ar_size, 10); + temp[10] = '\0'; + char* endptr; + long size = strtol(temp, &endptr, 10); + // long name is included in ar_size + if ( this->hasLongName() ) + size -= this->getLongNameSpace(); + return size; +} + + +template <typename A> +const class File<A>::Entry* File<A>::Entry::next() const +{ + const uint8_t* p = this->content() + contentSize(); + p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align + return (class File<A>::Entry*)p; +} + + +template <> cpu_type_t File<ppc>::architecture() { return CPU_TYPE_POWERPC; } +template <> cpu_type_t File<ppc64>::architecture() { return CPU_TYPE_POWERPC64; } +template <> cpu_type_t File<x86>::architecture() { return CPU_TYPE_I386; } +template <> cpu_type_t File<x86_64>::architecture() { return CPU_TYPE_X86_64; } +template <> cpu_type_t File<arm>::architecture() { return CPU_TYPE_ARM; } + + +template <typename A> +bool File<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) +{ + return mach_o::relocatable::isObjectFile(fileContent, fileLength, opts); +} + +template <typename A> +bool File<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) +{ + return lto::isObjectFile(fileContent, fileLength, opts.architecture, opts.subType); +} + + + +template <typename A> +bool File<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) +{ + // must have valid archive header + if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) + return false; + + // peak at first .o file and verify it is correct architecture + const Entry* const start = (Entry*)&fileContent[8]; + const Entry* const end = (Entry*)&fileContent[fileLength]; + for (const Entry* p=start; p < end; p = p->next()) { + const char* memberName = p->name(); + // skip option table-of-content member + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + // archive is valid if first .o file is valid + return (validMachOFile(p->content(), p->contentSize(), opts) || validLTOFile(p->content(), p->contentSize(), opts)); + } + // empty archive + return true; +} + + +template <typename A> +File<A>::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, time_t modTime, + uint32_t ord, const ParserOptions& opts) + : ld::File(strdup(pth), modTime, ord), + _archiveFileContent(fileContent), _archiveFilelength(fileLength), + _tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL), + _forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC), + _forceLoadThis(opts.forceLoadThisArchive), _verboseLoad(opts.verboseLoad), + _logAllFiles(opts.logAllFiles), _objOpts(opts.objOpts) +{ + if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) + throw "not an archive"; + + if ( !_forceLoadAll ) { + const Entry* const firstMember = (Entry*)&_archiveFileContent[8]; + if ( (strcmp(firstMember->name(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->name(), SYMDEF) == 0) ) { + const uint8_t* contents = firstMember->content(); + uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); + _tableOfContents = (const struct ranlib*)&contents[4]; + _tableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); + _tableOfContentStrings = (const char*)&contents[ranlibArrayLen+8]; + if ( ((uint8_t*)(&_tableOfContents[_tableOfContentCount]) > &fileContent[fileLength]) + || ((uint8_t*)_tableOfContentStrings > &fileContent[fileLength]) ) + throw "malformed archive, perhaps wrong architecture"; + this->buildHashTable(); + } + else + throw "archive has no table of contents"; + } +} + +template <> +bool File<x86>::memberHasObjCCategories(const Entry* member) const +{ + // i386 uses ObjC1 ABI which has .objc_category* global symbols + return false; +} + +template <> +bool File<ppc>::memberHasObjCCategories(const Entry* member) const +{ + // ppc uses ObjC1 ABI which has .objc_category* global symbols + return false; +} + + +template <typename A> +bool File<A>::memberHasObjCCategories(const Entry* member) const +{ + // x86_64 and ARM use ObjC2 which has no global symbol for categories + return mach_o::relocatable::hasObjC2Categories(member->content()); +} + + +template <typename A> +ld::relocatable::File* File<A>::makeObjectFileForMember(const Entry* member) const +{ + const char* memberName = member->name(); + char memberPath[strlen(this->path()) + strlen(memberName)+4]; + strcpy(memberPath, this->path()); + strcat(memberPath, "("); + strcat(memberPath, memberName); + strcat(memberPath, ")"); + //fprintf(stderr, "using %s from %s\n", memberName, this->path()); + try { + // range check + if ( member > (Entry*)(_archiveFileContent+_archiveFilelength) ) + throwf("corrupt archive, member starts past end of file"); + if ( (member->content() + member->contentSize()) > (_archiveFileContent+_archiveFilelength) ) + throwf("corrupt archive, member contents extends past end of file"); + const char* mPath = strdup(memberPath); + // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive + uint32_t memberIndex = ((uint8_t*)member - _archiveFileContent)/sizeof(ar_hdr); + // see if member is mach-o file + ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(), + mPath, member->modificationTime(), + this->ordinal() + memberIndex, _objOpts); + if ( result != NULL ) + return result; + // see if member is llvm bitcode file + result = lto::parse(member->content(), member->contentSize(), + mPath, member->modificationTime(), this->ordinal() + memberIndex, + _objOpts.architecture, _objOpts.subType, _logAllFiles); + if ( result != NULL ) + return result; + + throwf("archive member '%s' with length %d is not mach-o or llvm bitcode", memberName, member->contentSize()); + } + catch (const char* msg) { + throwf("in %s, %s", memberPath, msg); + } +} + + +template <typename A> +bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const +{ + bool didSome = false; + if ( _forceLoadAll || _forceLoadThis ) { + // call handler on all .o files in this archive + const Entry* const start = (Entry*)&_archiveFileContent[8]; + const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; + for (const Entry* p=start; p < end; p = p->next()) { + const char* memberName = p->name(); + if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) + continue; + if ( _verboseLoad ) { + if ( _forceLoadThis ) + printf("-force_load forced load of %s(%s)\n", this->path(), memberName); + else + printf("-all_load forced load of %s(%s)\n", this->path(), memberName); + } + ld::relocatable::File* file = this->makeObjectFileForMember(p); + didSome |= file->forEachAtom(handler); + } + } + else if ( _forceLoadObjC ) { + // call handler on all .o files in this archive containing objc classes + for(typename NameToEntryMap::const_iterator it = _hashTable.begin(); it != _hashTable.end(); ++it) { + if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) { + const Entry* member = (Entry*)&_archiveFileContent[E::get32(it->second->ran_off)]; + if ( _instantiatedEntries.count(member) == 0 ) { + if ( _verboseLoad ) + printf("-ObjC forced load of %s(%s)\n", this->path(), member->name()); + // only return these atoms once + _instantiatedEntries.insert(member); + ld::relocatable::File* file = this->makeObjectFileForMember(member); + didSome |= file->forEachAtom(handler); + _instantiatedFiles.push_back(file); + } + } + } + // ObjC2 has no symbols in .o files with categories, but not classes, look deeper for those + const Entry* const start = (Entry*)&_archiveFileContent[8]; + const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; + for (const Entry* member=start; member < end; member = member->next()) { + // only look at files not already instantiated + if ( _instantiatedEntries.count(member) == 0 ) { + //fprintf(stderr, "checking member %s\n", member->name()); + if ( this->memberHasObjCCategories(member) ) { + if ( _verboseLoad ) + printf("-ObjC forced load of %s(%s)\n", this->path(), member->name()); + // only return these atoms once + _instantiatedEntries.insert(member); + ld::relocatable::File* file = this->makeObjectFileForMember(member); + didSome |= file->forEachAtom(handler); + _instantiatedFiles.push_back(file); + } + } + } + } + return didSome; +} + +template <typename A> +bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // in force load case, all members already loaded + if ( _forceLoadAll || _forceLoadThis ) + return false; + + // do a hash search of table of contents looking for requested symbol + const struct ranlib* result = ranlibHashSearch(name); + if ( result != NULL ) { + const Entry* member = (Entry*)&_archiveFileContent[E::get32(result->ran_off)]; + // only call handler for each member once + if ( _instantiatedEntries.count(member) == 0 ) { + _instantiatedEntries.insert(member); + if ( _verboseLoad ) + printf("%s forced load of %s(%s)\n", name, this->path(), member->name()); + ld::relocatable::File* file = this->makeObjectFileForMember(member); + _instantiatedFiles.push_back(file); + return file->forEachAtom(handler); + } + } + //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); + return false; +} + + +typedef const struct ranlib* ConstRanLibPtr; + +template <typename A> +ConstRanLibPtr File<A>::ranlibHashSearch(const char* name) const +{ + typename NameToEntryMap::const_iterator pos = _hashTable.find(name); + if ( pos != _hashTable.end() ) + return pos->second; + else + return NULL; +} + +template <typename A> +void File<A>::buildHashTable() +{ + // walk through list backwards, adding/overwriting entries + // this assures that with duplicates those earliest in the list will be found + for (int i = _tableOfContentCount-1; i >= 0; --i) { + const struct ranlib* entry = &_tableOfContents[i]; + const char* entryName = &_tableOfContentStrings[E::get32(entry->ran_un.ran_strx)]; + if ( E::get32(entry->ran_off) > _archiveFilelength ) { + throwf("malformed archive TOC entry for %s, offset %d is beyond end of file %lld\n", + entryName, entry->ran_off, _archiveFilelength); + } + + //const Entry* member = (Entry*)&_archiveFileContent[E::get32(entry->ran_off)]; + //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); + _hashTable[entryName] = entry; + } +} + +template <typename A> +void File<A>::dumpTableOfContents() +{ + for (unsigned int i=0; i < _tableOfContentCount; ++i) { + const struct ranlib* e = &_tableOfContents[i]; + printf("%s in %s\n", &_tableOfContentStrings[E::get32(e->ran_un.ran_strx)], ((Entry*)&_archiveFileContent[E::get32(e->ran_off)])->name()); + } +} + + +// +// main function used by linker to instantiate archive files +// +ld::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts) +{ + switch ( opts.objOpts.architecture ) { + case CPU_TYPE_X86_64: + if ( archive::Parser<x86_64>::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_I386: + if ( archive::Parser<x86>::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_ARM: + if ( archive::Parser<arm>::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC: + if ( archive::Parser<ppc>::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser<ppc>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC64: + if ( archive::Parser<ppc64>::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser<ppc64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + } + return NULL; +} + + + +}; // namespace archive + + diff --git a/src/ld/parsers/archive_file.h b/src/ld/parsers/archive_file.h new file mode 100644 index 0000000..4dcbc8b --- /dev/null +++ b/src/ld/parsers/archive_file.h @@ -0,0 +1,48 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ARCHIVE_FILE_H__ +#define __ARCHIVE_FILE_H__ + +#include "ld.hpp" +#include "macho_relocatable_file.h" + +namespace archive { + +struct ParserOptions { + mach_o::relocatable::ParserOptions objOpts; + bool forceLoadThisArchive; + bool forceLoadAll; + bool forceLoadObjC; + bool verboseLoad; + bool logAllFiles; +}; + +extern ld::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts); + +} // namespace archive + + +#endif // __ARCHIVE_FILE_H__ diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp new file mode 100644 index 0000000..6221c90 --- /dev/null +++ b/src/ld/parsers/lto_file.cpp @@ -0,0 +1,870 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LTO_READER_H__ +#define __LTO_READER_H__ + +#include <stdlib.h> +#include <sys/param.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <mach-o/dyld.h> +#include <vector> +#include <ext/hash_set> +#include <ext/hash_map> + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "ld.hpp" +#include "macho_relocatable_file.h" +#include "lto_file.h" + +#include "llvm-c/lto.h" + + +namespace lto { + + +// +// ld64 only tracks non-internal symbols from an llvm bitcode file. +// We model this by having an InternalAtom which represent all internal functions and data. +// All non-interal symbols from a bitcode file are represented by an Atom +// and each Atom has a reference to the InternalAtom. The InternalAtom +// also has references to each symbol external to the bitcode file. +// +class InternalAtom : public ld::Atom +{ +public: + InternalAtom(class File& f); + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "import-atom"; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } + + // for adding references to symbols outside bitcode file + void addReference(const char* nm) + { _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, + ld::Fixup::kindNone, false, nm)); } +private: + + ld::File& _file; + mutable std::vector<ld::Fixup> _undefs; +}; + + +// +// LLVM bitcode file +// +class File : public ld::relocatable::File +{ +public: + File(const char* path, time_t mTime, const uint8_t* content, + uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); + virtual ~File(); + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const + { return false; } + virtual uint32_t cpuSubType() const { return _cpuSubType; } + + // overrides of ld::relocatable::File + virtual bool objcReplacementClasses() const { return false; } + virtual DebugInfoKind debugInfo() const { return _debugInfo; } + virtual const char* debugInfoPath() const { return _debugInfoPath; } + virtual time_t debugInfoModificationTime() const + { return _debugInfoModTime; } + virtual const std::vector<ld::relocatable::File::Stab>* stabs() const { return NULL; } + virtual bool canScatterAtoms() const { return true; } + + lto_module_t module() { return _module; } + class InternalAtom& internalAtom() { return _internalAtom; } + void setDebugInfo(ld::relocatable::File::DebugInfoKind k, + const char* pth, time_t modTime, uint32_t subtype) + { _debugInfo = k; + _debugInfoPath = pth; + _debugInfoModTime = modTime; + _cpuSubType = subtype;} + +private: + friend class Atom; + friend class InternalAtom; + friend class Parser; + + cpu_type_t _architecture; + class InternalAtom _internalAtom; + class Atom* _atomArray; + uint32_t _atomArrayCount; + lto_module_t _module; + const char* _debugInfoPath; + time_t _debugInfoModTime; + ld::Section _section; + ld::Fixup _fixupToInternal; + ld::relocatable::File::DebugInfoKind _debugInfo; + uint32_t _cpuSubType; +}; + +// +// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, +// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After +// optimization is performed, real Atoms are created for these symobls. However these real Atoms +// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate +// methods to real atom. +// +class Atom : public ld::Atom +{ +public: + Atom(File& f, const char* name, ld::Atom::Scope s, + ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a); + + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } + virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } + virtual void copyRawContent(uint8_t buffer[]) const + { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); } + virtual const uint8_t* rawContentPointer() const + { return (_compiledAtom ? _compiledAtom->rawContentPointer() : NULL); } + virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const + { return (_compiledAtom ? _compiledAtom->contentHash(ibt) : 0); } + virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const + { return (_compiledAtom ? _compiledAtom->canCoalesceWith(rhs,ibt) : false); } + virtual ld::Fixup::iterator fixupsBegin() const + { return (_compiledAtom ? _compiledAtom->fixupsBegin() : (ld::Fixup*)&_file._fixupToInternal); } + virtual ld::Fixup::iterator fixupsEnd() const + { return (_compiledAtom ? _compiledAtom->fixupsEnd() : &((ld::Fixup*)&_file._fixupToInternal)[1]); } + virtual ld::Atom::UnwindInfo::iterator beginUnwind() const + { return (_compiledAtom ? _compiledAtom->beginUnwind() : NULL); } + virtual ld::Atom::UnwindInfo::iterator endUnwind() const + { return (_compiledAtom ? _compiledAtom->endUnwind() : NULL); } + virtual ld::Atom::LineInfo::iterator beginLineInfo() const + { return (_compiledAtom ? _compiledAtom->beginLineInfo() : NULL); } + virtual ld::Atom::LineInfo::iterator endLineInfo() const + { return (_compiledAtom ? _compiledAtom->endLineInfo() : NULL); } + + const ld::Atom* compiledAtom() { return _compiledAtom; } + void setCompiledAtom(const ld::Atom& atom); + +private: + + File& _file; + const char* _name; + const ld::Atom* _compiledAtom; +}; + + + + + + + +class Parser +{ +public: + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); + static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength); + static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } + static bool optimize( const std::vector<const ld::Atom*>& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector<const ld::Atom*>& newAtoms, + std::vector<const char*>& additionalUndefines); + + static const char* ltoVersion() { return ::lto_get_version(); } + +private: + static const char* tripletPrefixForArch(cpu_type_t arch); + static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options); + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet; + typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom; + + class AtomSyncer : public ld::File::AtomHandler { + public: + AtomSyncer(std::vector<const char*>& a, std::vector<const ld::Atom*>&na, + CStringToAtom la, CStringToAtom dla, const OptimizeOptions& options) : + _options(options), _additionalUndefines(a), _newAtoms(na), _llvmAtoms(la), _deadllvmAtoms(dla) { } + virtual void doAtom(const class ld::Atom&); + virtual void doFile(const class ld::File&) { } + + const OptimizeOptions& _options; + std::vector<const char*>& _additionalUndefines; + std::vector<const ld::Atom*>& _newAtoms; + CStringToAtom _llvmAtoms; + CStringToAtom _deadllvmAtoms; + }; + + static std::vector<File*> _s_files; +}; + +std::vector<File*> Parser::_s_files; + + +bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) +{ + switch (architecture) { + case CPU_TYPE_I386: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "i386-"); + case CPU_TYPE_X86_64: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "x86_64-"); + case CPU_TYPE_ARM: + switch ( subarch ) { + case CPU_SUBTYPE_ARM_V6: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "armv6-"); + case CPU_SUBTYPE_ARM_V7: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "thumbv7-"); + } + break; + case CPU_TYPE_POWERPC: + return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "powerpc-"); + } + return false; +} + +const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) +{ + if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { + uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + switch (arch) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + case CPU_TYPE_ARM: + if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "armv6-") ) + return "armv6"; + if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "thumbv7-") ) + return "armv7"; + return "arm"; + } + return "unknown bitcode architecture"; + } + return NULL; +} + +File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) +{ + File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); + _s_files.push_back(f); + if ( logAllFiles ) + printf("%s\n", path); + return f; +} + + +ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options) +{ + mach_o::relocatable::ParserOptions objOpts; + objOpts.architecture = options.arch; + objOpts.objSubtypeMustMatch = false; + objOpts.logAllFiles = false; + objOpts.convertUnwindInfo = true; + objOpts.subType = 0; + + // mach-o parsing is done in-memory, but need path for debug notes + const char* path = "/tmp/lto.o"; + time_t modTime = 0; + if ( options.tmpObjectFilePath != NULL ) { + path = options.tmpObjectFilePath; + struct stat statBuffer; + if ( stat(options.tmpObjectFilePath, &statBuffer) == 0 ) + modTime = statBuffer.st_mtime; + } + + ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, nextInputOrdinal, objOpts); + if ( result != NULL ) + return result; + throw "LLVM LTO, file is not of required architecture"; +} + + + +File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ord, cpu_type_t arch) + : ld::relocatable::File(pth,mTime,ord), _architecture(arch), _internalAtom(*this), + _atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth), + _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), + _fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom), + _debugInfo(ld::relocatable::File::kDebugInfoNone), _cpuSubType(0) +{ + const bool log = false; + + // create llvm module + _module = ::lto_module_create_from_memory(content, contentLength); + if ( _module == NULL ) + throwf("could not parse object file %s: %s", pth, lto_get_error_message()); + + if ( log ) fprintf(stderr, "bitcode file: %s\n", pth); + + // create atom for each global symbol in module + uint32_t count = ::lto_module_get_num_symbols(_module); + _atomArray = (Atom*)malloc(sizeof(Atom)*count); + for (uint32_t i=0; i < count; ++i) { + const char* name = ::lto_module_get_symbol_name(_module, i); + lto_symbol_attributes attr = lto_module_get_symbol_attribute(_module, i); + + // <rdar://problem/6378110> LTO doesn't like dtrace symbols + // ignore dtrace static probes for now + // later when codegen is done and a mach-o file is produces the probes will be processed + if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) + continue; + + ld::Atom::Definition def; + ld::Atom::Combine combine = ld::Atom::combineNever; + switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { + case LTO_SYMBOL_DEFINITION_REGULAR: + def = ld::Atom::definitionRegular; + break; + case LTO_SYMBOL_DEFINITION_TENTATIVE: + def = ld::Atom::definitionTentative; + break; + case LTO_SYMBOL_DEFINITION_WEAK: + def = ld::Atom::definitionRegular; + combine = ld::Atom::combineByName; + break; + case LTO_SYMBOL_DEFINITION_UNDEFINED: + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: + def = ld::Atom::definitionProxy; + break; + default: + throwf("unknown definition kind for symbol %s in bitcode file %s", name, pth); + } + + // make LLVM atoms for definitions and a reference for undefines + if ( def != ld::Atom::definitionProxy ) { + ld::Atom::Scope scope; + switch ( attr & LTO_SYMBOL_SCOPE_MASK) { + case LTO_SYMBOL_SCOPE_INTERNAL: + scope = ld::Atom::scopeTranslationUnit; + break; + case LTO_SYMBOL_SCOPE_HIDDEN: + scope = ld::Atom::scopeLinkageUnit; + break; + case LTO_SYMBOL_SCOPE_DEFAULT: + scope = ld::Atom::scopeGlobal; + break; + default: + throwf("unknown scope for symbol %s in bitcode file %s", name, pth); + } + // only make atoms for non-internal symbols + if ( scope == ld::Atom::scopeTranslationUnit ) + continue; + uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); + // make Atom using placement new operator + new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment); + if ( scope == ld::Atom::scopeLinkageUnit ) + _internalAtom.addReference(name); + if ( log ) fprintf(stderr, "\t0x%08X %s\n", attr, name); + } + else { + // add to list of external references + _internalAtom.addReference(name); + if ( log ) fprintf(stderr, "\t%s (undefined)\n", name); + } + } +} + +File::~File() +{ + if ( _module != NULL ) + ::lto_module_dispose(_module); +} + +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doAtom(_internalAtom); + for(uint32_t i=0; i < _atomArrayCount; ++i) { + handler.doAtom(_atomArray[i]); + } + return true; +} + +InternalAtom::InternalAtom(File& f) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeLTOtemporary, ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _file(f) +{ +} + +Atom::Atom(File& f, const char* nm, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a) + : ld::Atom(f._section, d, c, s, ld::Atom::typeLTOtemporary, + ld::Atom::symbolTableIn, false, false, false, a), + _file(f), _name(nm), _compiledAtom(NULL) +{ +} + +void Atom::setCompiledAtom(const ld::Atom& atom) +{ + // set delegate so virtual methods go to it + _compiledAtom = &atom; + + //fprintf(stderr, "setting lto atom %p to delegate to mach-o atom %p (%s)\n", this, &atom, atom.name()); + + // update fields in ld::Atom to match newly constructed mach-o atom + (const_cast<Atom*>(this))->setAttributesFromAtom(atom); +} + + + +bool Parser::optimize( const std::vector<const ld::Atom*>& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector<const ld::Atom*>& newAtoms, + std::vector<const char*>& additionalUndefines) +{ + const bool logMustPreserve = false; + const bool logExtraOptions = false; + const bool logBitcodeFiles = false; + const bool logAtomsBeforeSync = false; + + // exit quickly if nothing to do + if ( _s_files.size() == 0 ) + return false; + + // print out LTO version string if -v was used + if ( options.verbose ) + fprintf(stderr, "%s\n", lto_get_version()); + + // create optimizer and add each Reader + lto_code_gen_t generator = ::lto_codegen_create(); + for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", (*it)->path()); + if ( ::lto_codegen_add_module(generator, (*it)->module()) ) + throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); + } + + // add any -mllvm command line options + for (std::vector<const char*>::const_iterator it=options.llvmOptions->begin(); it != options.llvmOptions->end(); ++it) { + if ( logExtraOptions ) fprintf(stderr, "passing option to llvm: %s\n", *it); + ::lto_codegen_debug_options(generator, *it); + } + + // The atom graph uses directed edges (references). Collect all references where + // originating atom is not part of any LTO Reader. This allows optimizer to optimize an + // external (i.e. not originated from same .o file) reference if all originating atoms are also + // defined in llvm bitcode file. + CStringSet nonLLVMRefs; + CStringToAtom llvmAtoms; + bool hasNonllvmAtoms = false; + for (std::vector<const ld::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + const ld::Atom* atom = *it; + // only look at references that come from an atom that is not an llvm atom + if ( atom->contentType() != ld::Atom::typeLTOtemporary ) { + if ( (atom->section().type() != ld::Section::typeMachHeader) && (atom->definition() != ld::Atom::definitionProxy) ) { + hasNonllvmAtoms = true; + } + const ld::Atom* target; + for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + // that reference an llvm atom + if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(fit->u.target->name()); + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + if ( target == NULL ) + throwf("'%s' in %s contains undefined reference", atom->name(), atom->file()->path()); + assert(target != NULL); + if ( target->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(target->name()); + default: + break; + } + } + } + else { + llvmAtoms[atom->name()] = (Atom*)atom; + } + } + // if entry point is in a llvm bitcode file, it must be preserved by LTO + if ( state.entryPoint!= NULL ) { + if ( state.entryPoint->contentType() == ld::Atom::typeLTOtemporary ) + nonLLVMRefs.insert(state.entryPoint->name()); + } + + // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions + // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced + // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead + // atom so that the linker can replace it with the mach-o one later. + CStringToAtom deadllvmAtoms; + for (std::vector<const ld::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->coalescedAway() && (atom->contentType() == ld::Atom::typeLTOtemporary) ) { + const char* name = atom->name(); + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)atom; + } + } + for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + File* file = *it; + for(uint32_t i=0; i < file->_atomArrayCount; ++i) { + Atom* llvmAtom = &file->_atomArray[i]; + if ( llvmAtom->coalescedAway() ) { + const char* name = llvmAtom->name(); + if ( deadllvmAtoms.find(name) == deadllvmAtoms.end() ) { + if ( logMustPreserve ) + fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + deadllvmAtoms[name] = (Atom*)llvmAtom; + } + } + else if ( options.linkerDeadStripping && !llvmAtom->live() ) { + const char* name = llvmAtom->name(); + deadllvmAtoms[name] = (Atom*)llvmAtom; + } + } + } + + // tell code generator about symbols that must be preserved + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + Atom* atom = it->second; + // Include llvm Symbol in export list if it meets one of following two conditions + // 1 - atom scope is global (and not linkage unit). + // 2 - included in nonLLVMRefs set. + // If a symbol is not listed in exportList then LTO is free to optimize it away. + if ( (atom->scope() == ld::Atom::scopeGlobal) ) { + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) { + if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced by a mach-o atom\n", name); + ::lto_codegen_add_must_preserve_symbol(generator, name); + } + } + + // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) + if ( options.relocatable && !hasNonllvmAtoms ) { + if ( ! ::lto_codegen_write_merged_modules(generator, options.outputFilePath) ) { + // HACK, no good way to tell linker we are all done, so just quit + exit(0); + } + warning("could not produce merged bitcode file"); + } + + // set code-gen model + lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + if ( options.mainExecutable ) { + if ( options.staticExecutable ) { + // darwin x86_64 "static" code model is really dynamic code model + if ( options.arch == CPU_TYPE_X86_64 ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_STATIC; + } + else { + if ( options.pie ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + } + } + else { + if ( options.allowTextRelocs ) + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; + else + model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + } + if ( ::lto_codegen_set_pic_model(generator, model) ) + throwf("could not create set codegen model: %s", lto_get_error_message()); + + // if requested, save off merged bitcode file + if ( options.saveTemps ) { + char tempBitcodePath[MAXPATHLEN]; + strcpy(tempBitcodePath, options.outputFilePath); + strcat(tempBitcodePath, ".lto.bc"); + ::lto_codegen_write_merged_modules(generator, tempBitcodePath); + } + +#if LTO_API_VERSION >= 3 + // find assembler next to linker + char path[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { + char* lastSlash = strrchr(path, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, "as"); + struct stat statInfo; + if ( stat(path, &statInfo) == 0 ) + ::lto_codegen_set_assembler_path(generator, path); + } + } +#endif + // run code generator + size_t machOFileLen; + const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + + // if requested, save off temp mach-o file + if ( options.saveTemps ) { + char tempMachoPath[MAXPATHLEN]; + strcpy(tempMachoPath, options.outputFilePath); + strcat(tempMachoPath, ".lto.o"); + int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, options.outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + } + + // if needed, save temp mach-o file to specific location + if ( options.tmpObjectFilePath != NULL ) { + int fd = ::open(options.tmpObjectFilePath, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if ( fd != -1) { + ::write(fd, machOFile, machOFileLen); + ::close(fd); + } + else { + warning("could not write LTO temp file '%s', errno=%d", options.tmpObjectFilePath, errno); + } + } + + // parse generated mach-o file into a MachOReader + ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, options); + + // sync generated mach-o atoms with existing atoms ld knows about + if ( logAtomsBeforeSync ) { + fprintf(stderr, "llvmAtoms:\n"); + for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { + const char* name = it->first; + //Atom* atom = it->second; + fprintf(stderr, "\t%s\n", name); + } + fprintf(stderr, "deadllvmAtoms:\n"); + for (CStringToAtom::iterator it = deadllvmAtoms.begin(); it != deadllvmAtoms.end(); ++it) { + const char* name = it->first; + //Atom* atom = it->second; + fprintf(stderr, "\t%s\n", name); + } + } + AtomSyncer syncer(additionalUndefines, newAtoms, llvmAtoms, deadllvmAtoms, options); + machoFile->forEachAtom(syncer); + + // Remove InternalAtoms from ld + for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + (*it)->internalAtom().setCoalescedAway(); + } + // Remove Atoms from ld if code generator optimized them away + for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { + // check if setRealAtom() called on this Atom + if ( li->second->compiledAtom() == NULL ) { + //fprintf(stderr, "llvm optimized away %p %s\n", li->second, li->second->name()); + li->second->setCoalescedAway(); + } + } + + // notify about file level attributes + handler.doFile(*machoFile); + + // if final mach-o file has debug info, update original bitcode files to match + for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { + (*it)->setDebugInfo(machoFile->debugInfo(), machoFile->path(), + machoFile->modificationTime(), machoFile->cpuSubType()); + } + + return true; +} + + +void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) +{ + // update proxy atoms to point to real atoms and find new atoms + const char* name = machoAtom.name(); + if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { + CStringToAtom::iterator pos = _llvmAtoms.find(name); + if ( pos != _llvmAtoms.end() ) { + // turn Atom into a proxy for this mach-o atom + pos->second->setCompiledAtom(machoAtom); + } + else { + // an atom of this name was not in the allAtoms list the linker gave us + if ( _deadllvmAtoms.find(name) != _deadllvmAtoms.end() ) { + // this corresponding to an atom that the linker coalesced away or marked not-live + if ( _options.linkerDeadStripping ) { + // llvm seems to want this atom and -dead_strip is enabled, so it will be deleted if not needed, so add back + Atom* llvmAtom = _deadllvmAtoms[name]; + llvmAtom->setCompiledAtom(machoAtom); + _newAtoms.push_back(&machoAtom); + } + else { + // Don't pass it back as a new atom + } + } + else + { + // this is something new that lto conjured up, tell ld its new + _newAtoms.push_back(&machoAtom); + } + } + } + else { + // ld only knew about non-static atoms, so this one must be new + _newAtoms.push_back(&machoAtom); + } + + // adjust fixups to go through proxy atoms + //fprintf(stderr, "adjusting fixups in atom: %s\n", machoAtom.name()); + for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) { + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + break; + case ld::Fixup::bindingByNameUnbound: + // don't know if this target has been seen by linker before or if it is new + // be conservative and tell linker it is new + _additionalUndefines.push_back(fit->u.name); + //fprintf(stderr, " by name ref to: %s\n", fit->u.name); + break; + case ld::Fixup::bindingDirectlyBound: + // If mach-o atom is referencing another mach-o atom then + // reference is not going through Atom proxy. Fix it here to ensure that all + // llvm symbol references always go through Atom proxy. + if ( fit->u.target->scope() != ld::Atom::scopeTranslationUnit ) { + const char* targetName = fit->u.target->name(); + CStringToAtom::iterator pos = _llvmAtoms.find(targetName); + if ( pos != _llvmAtoms.end() ) { + fit->u.target = pos->second; + } + else { + if ( _deadllvmAtoms.find(targetName) != _deadllvmAtoms.end() ) { + // target was coalesed away and replace by mach-o atom from a non llvm .o file + fit->binding = ld::Fixup::bindingByNameUnbound; + fit->u.name = targetName; + } + } + } + //fprintf(stderr, " direct ref to: %s (scope=%d)\n", fit->u.target->name(), fit->u.target->scope()); + break; + case ld::Fixup::bindingByContentBound: + //fprintf(stderr, " direct by content to: %s\n", fit->u.target->name()); + break; + case ld::Fixup::bindingsIndirectlyBound: + assert(0 && "indirect binding found in initial mach-o file?"); + //fprintf(stderr, " indirect by content to: %u\n", fit->u.bindingIndex); + break; + } + } + +} + + +// +// Used by archive reader to see if member is an llvm bitcode file +// +bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) +{ + return Parser::validFile(fileContent, fileLength, architecture, subarch); +} + + +// +// main function used by linker to instantiate ld::Files +// +ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) +{ + if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles); + else + return NULL; +} + +// +// used by "ld -v" to report version of libLTO.dylib being used +// +const char* version() +{ + return ::lto_get_version(); +} + + +// +// used by ld for error reporting +// +bool libLTOisLoaded() +{ + return (::lto_get_version() != NULL); +} + +// +// used by ld for error reporting +// +const char* archName(const uint8_t* fileContent, uint64_t fileLength) +{ + return Parser::fileKind(fileContent, fileLength); +} + +// +// used by ld for doing link time optimization +// +bool optimize( const std::vector<const ld::Atom*>& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector<const ld::Atom*>& newAtoms, + std::vector<const char*>& additionalUndefines) +{ + return Parser::optimize(allAtoms, state, nextInputOrdinal, options, handler, newAtoms, additionalUndefines); +} + + + +}; // namespace lto + + +#endif + diff --git a/src/ld/parsers/lto_file.h b/src/ld/parsers/lto_file.h new file mode 100644 index 0000000..dbb82a5 --- /dev/null +++ b/src/ld/parsers/lto_file.h @@ -0,0 +1,73 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __LTO_FILE_H__ +#define __LTO_FILE_H__ + +#include "ld.hpp" + +namespace lto { + +extern const char* version(); + +extern bool libLTOisLoaded(); + +extern const char* archName(const uint8_t* fileContent, uint64_t fileLength); + +extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); + +extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + +struct OptimizeOptions { + const char* outputFilePath; + const char* tmpObjectFilePath; + bool allGlobalsAReDeadStripRoots; + bool verbose; + bool saveTemps; + bool pie; + bool mainExecutable; + bool staticExecutable; + bool relocatable; + bool allowTextRelocs; + bool linkerDeadStripping; + cpu_type_t arch; + const std::vector<const char*>* llvmOptions; +}; + +extern bool optimize( const std::vector<const ld::Atom*>& allAtoms, + ld::Internal& state, + uint32_t nextInputOrdinal, + const OptimizeOptions& options, + ld::File::AtomHandler& handler, + std::vector<const ld::Atom*>& newAtoms, + std::vector<const char*>& additionalUndefines); + +} // namespace lto + + +#endif // __LTO_FILE_H__ + + diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp new file mode 100644 index 0000000..40dad13 --- /dev/null +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -0,0 +1,996 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/mman.h> + + +#include <vector> +#include <set> +#include <algorithm> +#include <ext/hash_map> +#include <ext/hash_set> + +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" +#include "macho_dylib_file.h" + + +namespace mach_o { +namespace dylib { + + +// forward reference +template <typename A> class File; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +template <typename A> +class ExportAtom : public ld::Atom +{ +public: + ExportAtom(const File<A>& f, const char* nm, bool weakDef, + bool tlv, typename A::P::uint_t address) + : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, + (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), + ld::Atom::scopeLinkageUnit, + (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), _name(nm), _address(address) {} + // overrides of ld::Atom + virtual const ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return _address; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +protected: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + virtual ~ExportAtom() {} + + const File<A>& _file; + const char* _name; + pint_t _address; +}; + + + +// +// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace +// the imports of all flat dylibs are checked +// +template <typename A> +class ImportAtom : public ld::Atom +{ +public: + ImportAtom(File<A>& f, std::vector<const char*>& imports); + + // overrides of ld::Atom + virtual ld::File* file() const { return &_file; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "import-atom"; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_undefs[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &_undefs[_undefs.size()]; } + +protected: + typedef typename A::P P; + + virtual ~ImportAtom() {} + + + File<A>& _file; + mutable std::vector<ld::Fixup> _undefs; +}; + +template <typename A> +ImportAtom<A>::ImportAtom(File<A>& f, std::vector<const char*>& imports) +: ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), _file(f) +{ + for(std::vector<const char*>::iterator it=imports.begin(); it != imports.end(); ++it) { + _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(*it))); + } +} + + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template <typename A> +class File : public ld::dylib::File +{ +public: + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + File(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, uint32_t ordinal, bool linkingFlatNamespace, + bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, + bool logAllFiles); + virtual ~File() {} + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; + virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; } + + // overrides of ld::dylib::File + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); + virtual bool providedExportAtom() const { return _providedAtom; } + virtual const char* parentUmbrella() const { return _parentUmbrella; } + virtual const std::vector<const char*>* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : NULL; } + virtual bool hasWeakExternals() const { return _hasWeakExports; } + virtual bool deadStrippable() const { return _deadStrippable; } + virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } + virtual bool hasWeakDefinition(const char* name) const; + + +protected: + + struct ReExportChain { ReExportChain* prev; File<A>* file; }; + + void assertNoReExportCycles(ReExportChain*); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + friend class ExportAtom<A>; + friend class ImportAtom<A>; + + class CStringEquals + { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + struct AtomAndWeak { ld::Atom* atom; bool weak; bool tlv; pint_t address; }; + typedef __gnu_cxx::hash_map<const char*, AtomAndWeak, __gnu_cxx::hash<const char*>, CStringEquals> NameToAtomMap; + typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet; + + struct Dependent { const char* path; File<A>* dylib; bool reExport; }; + + bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const; + bool isPublicLocation(const char* pth); + void addSymbol(const char* name, bool weak, bool tlv, pint_t address); + void addDyldFastStub(); + void buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo, + const uint8_t* fileContent); + void buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo, + const macho_nlist<P>* symbolTable, const char* strings, + const uint8_t* fileContent); + static const char* objCInfoSegmentName(); + static const char* objCInfoSectionName(); + + const ld::MacVersionMin _macVersionMin; + const ld::IPhoneVersionMin _iPhoneVersionMin; + bool _linkingFlat; + bool _implicitlyLinkPublicDylibs; + ld::File::ObjcConstraint _objcContraint; + ld::Section _importProxySection; + ld::Section _flatDummySection; + std::vector<Dependent> _dependentDylibs; + std::vector<const char*> _allowableClients; + mutable NameToAtomMap _atoms; + NameSet _ignoreExports; + const char* _parentUmbrella; + ImportAtom<A>* _importAtom; + bool _noRexports; + bool _hasWeakExports; + bool _deadStrippable; + bool _hasPublicInstallName; + mutable bool _providedAtom; + bool _explictReExportFound; + + static bool _s_logHashtable; +}; + +template <typename A> +bool File<A>::_s_logHashtable = false; + +template <> const char* File<x86_64>::objCInfoSegmentName() { return "__DATA"; } +template <> const char* File<arm>::objCInfoSegmentName() { return "__DATA"; } +template <typename A> const char* File<A>::objCInfoSegmentName() { return "__OBJC"; } + +template <> const char* File<x86_64>::objCInfoSectionName() { return "__objc_imageinfo"; } +template <> const char* File<arm>::objCInfoSectionName() { return "__objc_imageinfo"; } +template <typename A> const char* File<A>::objCInfoSectionName() { return "__image_info"; } + +template <typename A> +File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord, + bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, bool logAllFiles) + : ld::dylib::File(strdup(pth), mTime, ord), + _macVersionMin(macMin), _iPhoneVersionMin(iPhoneMin), + _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), + _objcContraint(ld::File::objcConstraintNone), + _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), + _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), + _parentUmbrella(NULL), _importAtom(NULL), _noRexports(false), _hasWeakExports(false), + _deadStrippable(false), _hasPublicInstallName(false), + _providedAtom(false), _explictReExportFound(false) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + const uint32_t cmd_count = header->ncmds(); + const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>)); + const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds()); + + // write out path for -t option + if ( logAllFiles ) + printf("%s\n", pth); + + // a "blank" stub has zero load commands + if ( (header->filetype() == MH_DYLIB_STUB) && (cmd_count == 0) ) { + // no further processing needed + munmap((caddr_t)fileContent, fileLength); + return; + } + + + // optimize the case where we know there is no reason to look at indirect dylibs + _noRexports = (header->flags() & MH_NO_REEXPORTED_DYLIBS) + || (header->filetype() == MH_BUNDLE) + || (header->filetype() == MH_EXECUTE); // bundles and exectuables can be used via -bundle_loader + _hasWeakExports = (header->flags() & MH_WEAK_DEFINES); + _deadStrippable = (header->flags() & MH_DEAD_STRIPPABLE_DYLIB); + + // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format + const macho_dysymtab_command<P>* dynamicInfo = NULL; + const macho_dyld_info_command<P>* dyldInfo = NULL; + const macho_nlist<P>* symbolTable = NULL; + const char* strings = NULL; + bool compressedLinkEdit = false; + uint32_t dependentLibCount = 0; + const macho_load_command<P>* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + macho_dylib_command<P>* dylibID; + const macho_symtab_command<P>* symtab; + switch (cmd->cmd()) { + case LC_SYMTAB: + symtab = (macho_symtab_command<P>*)cmd; + symbolTable = (const macho_nlist<P>*)((char*)header + symtab->symoff()); + strings = (char*)header + symtab->stroff(); + break; + case LC_DYSYMTAB: + dynamicInfo = (macho_dysymtab_command<P>*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (macho_dyld_info_command<P>*)cmd; + compressedLinkEdit = true; + break; + case LC_ID_DYLIB: + dylibID = (macho_dylib_command<P>*)cmd; + _dylibInstallPath = strdup(dylibID->name()); + _dylibTimeStamp = dylibID->timestamp(); + _dylibCurrentVersion = dylibID->current_version(); + _dylibCompatibilityVersion = dylibID->compatibility_version(); + _hasPublicInstallName = isPublicLocation(_dylibInstallPath); + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + ++dependentLibCount; + break; + case LC_REEXPORT_DYLIB: + _explictReExportFound = true; + ++dependentLibCount; + break; + case LC_SUB_FRAMEWORK: + _parentUmbrella = strdup(((macho_sub_framework_command<P>*)cmd)->umbrella()); + break; + case LC_SUB_CLIENT: + _allowableClients.push_back(strdup(((macho_sub_client_command<P>*)cmd)->client())); + break; + case macho_segment_command<P>::CMD: + // check for Objective-C info + if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { + const macho_segment_command<P>* segment = (macho_segment_command<P>*)cmd; + const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>)); + const macho_section<P>* const sectionsEnd = §ionsStart[segment->nsects()]; + for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( strncmp(sect->sectname(), objCInfoSectionName(), strlen(objCInfoSectionName())) == 0 ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + _objcContraint = ld::File::objcConstraintGC; + else if ( (flags & 2) == 2 ) + _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + else + _objcContraint = ld::File::objcConstraintRetainRelease; + } + else if ( sect->size() > 0 ) { + warning("can't parse %s/%s section in %s", objCInfoSegmentName(), objCInfoSectionName(), this->path()); + } + } + } + } + } + cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth); + } + + // figure out if we need to examine dependent dylibs + // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted + bool processDependentLibraries = true; + if ( compressedLinkEdit && _noRexports && !linkingFlatNamespace) + processDependentLibraries = false; + + if ( processDependentLibraries ) { + // pass 2 builds list of all dependent libraries + _dependentDylibs.reserve(dependentLibCount); + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + // with new linkedit format only care about LC_REEXPORT_DYLIB + if ( compressedLinkEdit && !linkingFlatNamespace ) + break; + case LC_REEXPORT_DYLIB: + Dependent entry; + entry.path = strdup(((macho_dylib_command<P>*)cmd)->name()); + entry.dylib = NULL; + entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB); + _dependentDylibs.push_back(entry); + break; + } + cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); + } + // verify MH_NO_REEXPORTED_DYLIBS bit was correct + if ( compressedLinkEdit && !linkingFlatNamespace ) { + assert(_dependentDylibs.size() != 0); + } + // pass 3 add re-export info + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + const char* frameworkLeafName; + const char* dylibBaseName; + switch (cmd->cmd()) { + case LC_SUB_UMBRELLA: + frameworkLeafName = ((macho_sub_umbrella_command<P>*)cmd)->sub_umbrella(); + for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->reExport = true; + } + break; + case LC_SUB_LIBRARY: + dylibBaseName = ((macho_sub_library_command<P>*)cmd)->sub_library(); + for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + const char* dylibName = it->path; + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->reExport = true; + } + break; + } + cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); + } + } + + // validate minimal load commands + if ( (_dylibInstallPath == NULL) && ((header->filetype() == MH_DYLIB) || (header->filetype() == MH_DYLIB_STUB)) ) + throwf("dylib %s missing LC_ID_DYLIB load command", pth); + if ( dyldInfo == NULL ) { + if ( symbolTable == NULL ) + throw "binary missing LC_SYMTAB load command"; + if ( dynamicInfo == NULL ) + throw "binary missing LC_DYSYMTAB load command"; + } + + // if linking flat and this is a flat dylib, create one atom that references all imported symbols + if ( linkingFlatNamespace && linkingMainExecutable && ((header->flags() & MH_TWOLEVEL) == 0) ) { + std::vector<const char*> importNames; + importNames.reserve(dynamicInfo->nundefsym()); + const macho_nlist<P>* start = &symbolTable[dynamicInfo->iundefsym()]; + const macho_nlist<P>* end = &start[dynamicInfo->nundefsym()]; + for (const macho_nlist<P>* sym=start; sym < end; ++sym) { + importNames.push_back(&strings[sym->n_strx()]); + } + _importAtom = new ImportAtom<A>(*this, importNames); + } + + // build hash table + if ( dyldInfo != NULL ) + buildExportHashTableFromExportInfo(dyldInfo, fileContent); + else + buildExportHashTableFromSymbolTable(dynamicInfo, symbolTable, strings, fileContent); + + // unmap file + munmap((caddr_t)fileContent, fileLength); +} + + +template <typename A> +void File<A>::buildExportHashTableFromSymbolTable(const macho_dysymtab_command<P>* dynamicInfo, + const macho_nlist<P>* symbolTable, const char* strings, + const uint8_t* fileContent) +{ + if ( dynamicInfo->tocoff() == 0 ) { + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u toc entries for %s\n", dynamicInfo->nextdefsym(), this->path()); + const macho_nlist<P>* start = &symbolTable[dynamicInfo->iextdefsym()]; + const macho_nlist<P>* end = &start[dynamicInfo->nextdefsym()]; + _atoms.resize(dynamicInfo->nextdefsym()); // set initial bucket count + for (const macho_nlist<P>* sym=start; sym < end; ++sym) { + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); + } + } + else { + int32_t count = dynamicInfo->ntoc(); + _atoms.resize(count); // set initial bucket count + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable of %u entries for %s\n", count, this->path()); + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)(fileContent + dynamicInfo->tocoff()); + for (int32_t i = 0; i < count; ++i) { + const uint32_t index = E::get32(toc[i].symbol_index); + const macho_nlist<P>* sym = &symbolTable[index]; + this->addSymbol(&strings[sym->n_strx()], (sym->n_desc() & N_WEAK_DEF) != 0, false, sym->n_value()); + } + } + + // special case old libSystem + if ( (_dylibInstallPath != NULL) && (strcmp(_dylibInstallPath, "/usr/lib/libSystem.B.dylib") == 0) ) + addDyldFastStub(); +} + + +template <typename A> +void File<A>::buildExportHashTableFromExportInfo(const macho_dyld_info_command<P>* dyldInfo, + const uint8_t* fileContent) +{ + if ( _s_logHashtable ) fprintf(stderr, "ld: building hashtable from export info in %s\n", this->path()); + if ( dyldInfo->export_size() > 0 ) { + const uint8_t* start = fileContent + dyldInfo->export_off(); + const uint8_t* end = &start[dyldInfo->export_size()]; + std::vector<mach_o::trie::Entry> list; + parseTrie(start, end, list); + for (std::vector<mach_o::trie::Entry>::iterator it=list.begin(); it != list.end(); ++it) + this->addSymbol(it->name, + it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION, + (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL, + it->address); + } +} + + +template <> +void File<x86_64>::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false, false, 0); +} + +template <> +void File<x86>::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false, false, 0); +} + +template <typename A> +void File<A>::addDyldFastStub() +{ + // do nothing +} + +template <typename A> +void File<A>::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address) +{ + if ( weakDef ) { + assert(_hasWeakExports); + } + //fprintf(stderr, "addSymbol() %s\n", name); + // symbols that start with $ld$ are meta-data to the static linker + // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ <action> $ <condition> $ <symbol-name> + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != NULL ) { + char curOSVers[16]; + if ( _macVersionMin != ld::macVersionUnset ) { + sprintf(curOSVers, "$os%d.%d$", (_macVersionMin >> 16), ((_macVersionMin >> 8) & 0xFF)); + } + else if ( _iPhoneVersionMin != ld::iPhoneVersionUnset ) { + sprintf(curOSVers, "$os%d.%d$", (_iPhoneVersionMin >> 16), ((_iPhoneVersionMin >> 8) & 0xFF)); + } + else { + assert(0 && "targeting neither macosx nor iphoneos"); + } + if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( _s_logHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + _ignoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef, false, 0); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->path()); + } + } + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->path()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( _ignoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = NULL; + bucket.weak = weakDef; + bucket.tlv = tlv; + bucket.address = address; + if ( _s_logHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + _atoms[strdup(name)] = bucket; + } +} + + +template <typename A> +bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + // if doing flatnamespace and need all this dylib's imports resolve + // add atom which references alls undefines in this dylib + if ( _importAtom != NULL ) { + handler.doAtom(*_importAtom); + return true; + } + return false; +} + +template <typename A> +bool File<A>::hasWeakDefinition(const char* name) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + typename NameToAtomMap::const_iterator pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + return pos->second.weak; + } + else { + // look in children that I re-export + for (typename std::vector<Dependent>::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + if ( it->reExport ) { + //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->path(), (*it)->getInstallPath()); + typename NameToAtomMap::iterator cpos = it->dylib->_atoms.find(name); + if ( cpos != it->dylib->_atoms.end() ) + return cpos->second.weak; + } + } + } + return false; +} + +template <typename A> +bool File<A>::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const +{ + // check myself + typename NameToAtomMap::iterator pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + *weakDef = pos->second.weak; + *tlv = pos->second.tlv; + *defAddress = pos->second.address; + return true; + } + + // check dylibs I re-export + for (typename std::vector<Dependent>::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { + if ( it->reExport && !it->dylib->implicitlyLinked() ) { + if ( it->dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) + return true; + } + } + + return false; +} + + +template <typename A> +bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + + AtomAndWeak bucket; + if ( this->containsOrReExports(name, &bucket.weak, &bucket.tlv, &bucket.address) ) { + bucket.atom = new ExportAtom<A>(*this, name, bucket.weak, bucket.tlv, bucket.address); + _atoms[name] = bucket; + _providedAtom = true; + if ( _s_logHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); + // call handler with new export atom + handler.doAtom(*bucket.atom); + return true; + } + + return false; +} + + + +template <typename A> +bool File<A>::isPublicLocation(const char* pth) +{ + // -no_implicit_dylibs disables this optimization + if ( ! _implicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&pth[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != NULL ) { + int frameworkNameLen = frameworkDot - &pth[27]; + if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template <typename A> +void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) +{ + const static bool log = false; + if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); + if ( _linkingFlat ) { + for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + it->dylib = (File<A>*)handler->findDylib(it->path, this->path()); + } + } + else if ( _noRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + if ( it->reExport ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + it->dylib = (File<A>*)handler->findDylib(it->path, this->path()); + if ( it->dylib->hasPublicInstallName() ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); + it->dylib->setImplicitlyLinked(); + } + else if ( it->dylib->explicitlyLinked() || it->dylib->implicitlyLinked() ) { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } + else { + if ( log ) fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + else { + // add all child's symbols to me + if ( log ) fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + else if ( !_explictReExportFound ) { + // see if child contains LC_SUB_FRAMEWORK with my name + it->dylib = (File<A>*)handler->findDylib(it->path, this->path()); + const char* parentUmbrellaName = it->dylib->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + const char* parentName = this->path(); + const char* lastSlash = strrchr(parentName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) { + // add all child's symbols to me + it->reExport = true; + if ( log ) fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), it->path); + } + } + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = NULL; + chain.file = this; + this->assertNoReExportCycles(&chain); +} + +template <typename A> +void File<A>::assertNoReExportCycles(ReExportChain* prev) +{ + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.file = this; + for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { + if ( it->reExport ) { + ld::File* child = it->dylib; + // check child is not already in chain + for (ReExportChain* p = prev; p != NULL; p = p->prev) { + if ( p->file == child ) { + throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); + } + } + if ( it->dylib != NULL ) + it->dylib->assertNoReExportCycles(&chain); + } + } +} + + +struct ParserOptions { + bool linkingFlat; + bool linkingMain; + bool addImplictDylibs; + ld::MacVersionMin macOSMin; + ld::IPhoneVersionMin iphoneOSMin; +}; + + +template <typename A> +class Parser +{ +public: + typedef typename A::P P; + + static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle); + static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t mTime, + uint32_t ordinal, const Options& opts) { + return new File<A>(fileContent, fileLength, path, mTime, + ordinal, opts.flatNamespace(), + opts.linkingMainExecutable(), + opts.implicitlyLinkIndirectPublicDylibs(), + opts.macosxVersionMin(), + opts.iphoneOSVersionMin(), + opts.logAllFiles()); + } + +}; + + + +template <> +bool Parser<ppc>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser<ppc64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser<x86>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser<x86_64>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + +template <> +bool Parser<arm>::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_ARM ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + + + +// +// main function used by linker to instantiate ld::Files +// +ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, const Options& opts, uint32_t ordinal, bool bundleLoader) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + if ( Parser<x86_64>::validFile(fileContent, bundleLoader) ) + return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_I386: + if ( Parser<x86>::validFile(fileContent, bundleLoader) ) + return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_ARM: + if ( Parser<arm>::validFile(fileContent, bundleLoader) ) + return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC: + if ( Parser<ppc>::validFile(fileContent, bundleLoader) ) + return Parser<ppc>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC64: + if ( Parser<ppc64>::validFile(fileContent, bundleLoader) ) + return Parser<ppc64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + } + return NULL; +} + + +}; // namespace dylib +}; // namespace mach_o + + diff --git a/src/ld/parsers/macho_dylib_file.h b/src/ld/parsers/macho_dylib_file.h new file mode 100644 index 0000000..5c858e2 --- /dev/null +++ b/src/ld/parsers/macho_dylib_file.h @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __MACHO_DYLIB_FILE_H__ +#define __MACHO_DYLIB_FILE_H__ + +#include "ld.hpp" +#include "Options.h" + +namespace mach_o { +namespace dylib { + +extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const Options& opts, uint32_t ordinal, bool bundleLoader); + +} // namespace dylib +} // namespace mach_o + + +#endif // __MACHO_DYLIB_FILE_H__ diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp new file mode 100644 index 0000000..25c1965 --- /dev/null +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -0,0 +1,6552 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <stdlib.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include "MachOFileAbstraction.hpp" + +#include <libunwind/DwarfInstructions.hpp> +#include <libunwind/AddressSpace.hpp> +#include <libunwind/Registers.hpp> + +#include <vector> +#include <set> +#include <map> +#include <algorithm> + +#include "dwarf2.h" +#include "debugline.h" + +#include "Architectures.hpp" +#include "ld.hpp" +#include "macho_relocatable_file.h" + + + +extern void throwf(const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); +extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); + +namespace mach_o { +namespace relocatable { + + +// forward reference +template <typename A> class Parser; +template <typename A> class Atom; +template <typename A> class Section; +template <typename A> class CFISection; + +template <typename A> +class File : public ld::relocatable::File +{ +public: + File(const char* p, time_t mTime, const uint8_t* content, uint32_t ord) : + ld::relocatable::File(p,mTime,ord), _fileContent(content), + _sectionsArray(NULL), _atomsArray(NULL), + _sectionsArrayCount(0), _atomsArrayCount(0), + _debugInfoKind(ld::relocatable::File::kDebugInfoNone), + _dwarfTranslationUnitDir(NULL), _dwarfTranslationUnitFile(NULL), + _dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL), + _dwarfDebugLineSect(NULL), _dwarfDebugStringSect(NULL), + _objConstraint(ld::File::objcConstraintNone), + _cpuSubType(0), + _ojcReplacmentClass(false), _canScatterAtoms(false) {} + virtual ~File(); + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const + { return false; } + + // overrides of ld::relocatable::File + virtual bool objcReplacementClasses() const { return _ojcReplacmentClass; } + virtual ObjcConstraint objCConstraint() const { return _objConstraint; } + virtual uint32_t cpuSubType() const { return _cpuSubType; } + virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } + virtual const std::vector<ld::relocatable::File::Stab>* stabs() const { return &_stabs; } + virtual bool canScatterAtoms() const { return _canScatterAtoms; } + bool translationUnitSource(const char** dir, const char** name) const; + + const uint8_t* fileContent() { return _fileContent; } +private: + friend class Atom<A>; + friend class Section<A>; + friend class Parser<A>; + friend class CFISection<A>::OAS; + + typedef typename A::P P; + + const uint8_t* _fileContent; + Section<A>** _sectionsArray; + uint8_t* _atomsArray; + uint32_t _sectionsArrayCount; + uint32_t _atomsArrayCount; + std::vector<ld::Fixup> _fixups; + std::vector<ld::Atom::UnwindInfo> _unwindInfos; + std::vector<ld::Atom::LineInfo> _lineInfos; + std::vector<ld::relocatable::File::Stab>_stabs; + ld::relocatable::File::DebugInfoKind _debugInfoKind; + const char* _dwarfTranslationUnitDir; + const char* _dwarfTranslationUnitFile; + const macho_section<P>* _dwarfDebugInfoSect; + const macho_section<P>* _dwarfDebugAbbrevSect; + const macho_section<P>* _dwarfDebugLineSect; + const macho_section<P>* _dwarfDebugStringSect; + ld::File::ObjcConstraint _objConstraint; + uint32_t _cpuSubType; + bool _ojcReplacmentClass; + bool _canScatterAtoms; +}; + + +template <typename A> +class Section : public ld::Section +{ +public: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + + virtual ~Section() { } + class File<A>& file() const { return _file; } + const macho_section<P>* machoSection() const { return _machOSection; } + uint32_t sectionNum(class Parser<A>&) const; + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr); + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeUnclassified; } + virtual bool dontDeadStrip() { return (this->_machOSection->flags() & S_ATTR_NO_DEAD_STRIP); } + virtual Atom<A>* findAtomByAddress(pint_t addr) { return this->findContentAtomByAddress(addr, this->_beginAtoms, this->_endAtoms); } + virtual bool addFollowOnFixups() const { return ! _file.canScatterAtoms(); } + virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) = 0; + virtual uint32_t computeAtomCount(class Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) = 0; + virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&); + virtual bool addRelocFixup(class Parser<A>& parser, const macho_relocation_info<P>*); + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const { return 0; } + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const { return false; } + +protected: + Section(File<A>& f, const macho_section<typename A::P>* s) + : ld::Section(makeSegmentName(s), makeSectionName(s), sectionType(s)), + _file(f), _machOSection(s), _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } + Section(File<A>& f, const char* segName, const char* sectName, ld::Section::Type t, bool hidden=false) + : ld::Section(segName, sectName, t, hidden), _file(f), _machOSection(NULL), + _beginAtoms(NULL), _endAtoms(NULL), _hasAliases(false) { } + + + bool addRelocFixup_powerpc(class Parser<A>& parser,const macho_relocation_info<typename A::P>* reloc); + Atom<A>* findContentAtomByAddress(pint_t addr, class Atom<A>* start, class Atom<A>* end); + uint32_t x86_64PcRelOffset(uint8_t r_type); + static const char* makeSegmentName(const macho_section<typename A::P>* s); + static const char* makeSectionName(const macho_section<typename A::P>* s); + static bool readable(const macho_section<typename A::P>* s); + static bool writable(const macho_section<typename A::P>* s); + static bool exectuable(const macho_section<typename A::P>* s); + static ld::Section::Type sectionType(const macho_section<typename A::P>* s); + + File<A>& _file; + const macho_section<P>* _machOSection; + class Atom<A>* _beginAtoms; + class Atom<A>* _endAtoms; + bool _hasAliases; +}; + + +template <typename A> +class CFISection : public Section<A> +{ +public: + CFISection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : Section<A>(f, s) { } + uint32_t cfiCount(); + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCFI; } + virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&); + virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&); + virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&); + virtual bool addFollowOnFixups() const { return false; } + + + /// + /// ObjectFileAddressSpace is used as a template parameter to UnwindCursor for parsing + /// dwarf CFI information in an object file. + /// + class OAS + { + public: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t sint_t; + + OAS(CFISection<A>& ehFrameSection, const uint8_t* ehFrameBuffer) : + _ehFrameSection(ehFrameSection), + _ehFrameContent(ehFrameBuffer), + _ehFrameStartAddr(ehFrameSection.machoSection()->addr()), + _ehFrameEndAddr(ehFrameSection.machoSection()->addr()+ehFrameSection.machoSection()->size()) {} + + uint8_t get8(pint_t addr) { return *((uint8_t*)mappedAddress(addr)); } + uint16_t get16(pint_t addr) { return E::get16(*((uint16_t*)mappedAddress(addr))); } + uint32_t get32(pint_t addr) { return E::get32(*((uint32_t*)mappedAddress(addr))); } + uint64_t get64(pint_t addr) { return E::get64(*((uint64_t*)mappedAddress(addr))); } + pint_t getP(pint_t addr) { return P::getP(*((pint_t*)mappedAddress(addr))); } + uint64_t getULEB128(pint_t& addr, pint_t end); + int64_t getSLEB128(pint_t& addr, pint_t end); + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + private: + const void* mappedAddress(pint_t addr); + + CFISection<A>& _ehFrameSection; + const uint8_t* _ehFrameContent; + pint_t _ehFrameStartAddr; + pint_t _ehFrameEndAddr; + }; + + + typedef typename A::P::uint_t pint_t; + typedef libunwind::CFI_Atom_Info<OAS> CFI_Atom_Info; + + void cfiParse(class Parser<A>& parser, uint8_t* buffer, CFI_Atom_Info cfiArray[], uint32_t cfiCount); + bool needsRelocating(); + + static bool bigEndian(); +private: + void addCiePersonalityFixups(class Parser<A>& parser, const CFI_Atom_Info* cieInfo); + static void warnFunc(void* ref, uint64_t funcAddr, const char* msg); +}; + + +template <typename A> +class TentativeDefinitionSection : public Section<A> +{ +public: + TentativeDefinitionSection(Parser<A>& parser, File<A>& f) + : Section<A>(f, "__DATA", "__comm/tent", ld::Section::typeTentativeDefs) {} + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeZeroFill; } + virtual bool addFollowOnFixups() const { return false; } + virtual Atom<A>* findAtomByAddress(typename A::P::uint_t addr) { throw "TentativeDefinitionSection::findAtomByAddress() should never be called"; } + virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&); + virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&); + virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&) {} +private: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; +}; + + +template <typename A> +class AbsoluteSymbolSection : public Section<A> +{ +public: + AbsoluteSymbolSection(Parser<A>& parser, File<A>& f) + : Section<A>(f, "__DATA", "__abs", ld::Section::typeAbsoluteSymbols, true) {} + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeUnclassified; } + virtual bool dontDeadStrip() { return false; } + virtual ld::Atom::Alignment alignmentForAddress(typename A::P::uint_t addr) { return ld::Atom::Alignment(0); } + virtual bool addFollowOnFixups() const { return false; } + virtual Atom<A>* findAtomByAddress(typename A::P::uint_t addr) { throw "AbsoluteSymbolSection::findAtomByAddress() should never be called"; } + virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&); + virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&); + virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&) {} + virtual Atom<A>* findAbsAtomForValue(typename A::P::uint_t); + +private: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; +}; + + +template <typename A> +class SymboledSection : public Section<A> +{ +public: + SymboledSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s); + virtual ld::Atom::ContentType contentType() { return _type; } + virtual bool dontDeadStrip(); + virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&); + virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&); +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + ld::Atom::ContentType _type; +}; + + +template <typename A> +class TLVDefsSection : public SymboledSection<A> +{ +public: + TLVDefsSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) : + SymboledSection<A>(parser, f, s) { } + +private: + +}; + + +template <typename A> +class ImplicitSizeSection : public Section<A> +{ +public: + ImplicitSizeSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : Section<A>(f, s) { } + virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&); + virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&); +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual bool addFollowOnFixups() const { return false; } + virtual const char* unlabeledAtomName(Parser<A>& parser, pint_t addr) = 0; + virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableNotIn; } + virtual pint_t elementSizeAtAddress(pint_t addr) = 0; + virtual ld::Atom::Scope scopeAtAddress(Parser<A>& parser, pint_t addr) { return ld::Atom::scopeLinkageUnit; } + virtual bool useElementAt(Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, pint_t addr) = 0; + virtual ld::Atom::Definition definition() { return ld::Atom::definitionRegular; } + virtual ld::Atom::Combine combine(Parser<A>& parser, pint_t addr) = 0; + virtual bool ignoreLabel(const char* label) { return (label[0] == 'L'); } +}; + +template <typename A> +class FixedSizeSection : public ImplicitSizeSection<A> +{ +public: + FixedSizeSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : ImplicitSizeSection<A>(parser, f, s) { } +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + + virtual bool useElementAt(Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, pint_t addr) + { return true; } +}; + + +template <typename A> +class Literal4Section : public FixedSizeSection<A> +{ +public: + Literal4Section(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(2); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "4-byte-literal"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 4; } + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + +template <typename A> +class Literal8Section : public FixedSizeSection<A> +{ +public: + Literal8Section(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(3); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "8-byte-literal"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 8; } + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + +template <typename A> +class Literal16Section : public FixedSizeSection<A> +{ +public: + Literal16Section(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(4); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "16-byte-literal"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 16; } + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + + +template <typename A> +class NonLazyPointerSection : public FixedSizeSection<A> +{ +public: + NonLazyPointerSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&); + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeNonLazyPointer; } + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "non_lazy_ptr"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Scope scopeAtAddress(Parser<A>& parser, pint_t addr); + virtual ld::Atom::Combine combine(Parser<A>&, pint_t); + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + +private: + static const char* targetName(const class Atom<A>* atom, const ld::IndirectBindingTable& ind); + static ld::Fixup::Kind fixupKind(); +}; + + +template <typename A> +class CFStringSection : public FixedSizeSection<A> +{ +public: + CFStringSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "CFString"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return 4*sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndReferences; } + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +private: + enum ContentType { contentUTF8, contentUTF16, contentUnknown }; + static const uint8_t* targetContent(const class Atom<A>* atom, const ld::IndirectBindingTable& ind, + ContentType* ct, unsigned int* count); +}; + + +template <typename A> +class ObjC1ClassSection : public FixedSizeSection<A> +{ +public: + ObjC1ClassSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + typedef typename A::P::E E; + + virtual ld::Atom::Scope scopeAtAddress(Parser<A>& , pint_t ) { return ld::Atom::scopeGlobal; } + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(2); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t); + virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableIn; } + virtual pint_t elementSizeAtAddress(pint_t addr); + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineNever; } + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const + { return 0; } + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const { return false; } + virtual bool addRelocFixup(class Parser<A>& parser, const macho_relocation_info<P>*); +}; + + +template <typename A> +class ObjC2ClassRefsSection : public FixedSizeSection<A> +{ +public: + ObjC2ClassRefsSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "objc-class-ref"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndReferences; } + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +private: + const char* targetClassName(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template <typename A> +class ObjC2CategoryListSection : public FixedSizeSection<A> +{ +public: + ObjC2CategoryListSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual ld::Atom::Scope scopeAtAddress(Parser<A>& parser, pint_t addr) { return ld::Atom::scopeTranslationUnit; } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "objc-cat-list"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineNever; } + virtual bool ignoreLabel(const char* label) { return true; } +private: + const char* targetClassName(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template <typename A> +class PointerToCStringSection : public FixedSizeSection<A> +{ +public: + PointerToCStringSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : FixedSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "pointer-to-literal-cstring"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndReferences; } + virtual bool ignoreLabel(const char* label) { return true; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + virtual const char* targetCString(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template <typename A> +class Objc1ClassReferences : public PointerToCStringSection<A> +{ +public: + Objc1ClassReferences(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : PointerToCStringSection<A>(parser, f, s) {} + + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "pointer-to-literal-objc-class-name"; } + virtual bool addRelocFixup(class Parser<A>& parser, const macho_relocation_info<P>*); + virtual const char* targetCString(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; +}; + + +template <typename A> +class CStringSection : public ImplicitSizeSection<A> +{ +public: + CStringSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : ImplicitSizeSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCString; } + virtual Atom<A>* findAtomByAddress(pint_t addr); + virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "cstring"; } + virtual pint_t elementSizeAtAddress(pint_t addr); + virtual bool useElementAt(Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, pint_t addr); + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + +}; + + +template <typename A> +class UTF16StringSection : public SymboledSection<A> +{ +public: + UTF16StringSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : SymboledSection<A>(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndContent; } + virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; +}; + + +// +// Atoms in mach-o files +// +template <typename A> +class Atom : public ld::Atom +{ +public: + // overrides of ld::Atom + virtual ld::File* file() const { return §().file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return sect().file().translationUnitSource(dir, nm); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return _objAddress; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual const uint8_t* rawContentPointer() const { return contentPointer(); } + virtual unsigned long contentHash(const ld::IndirectBindingTable& ind) const + { if ( _hash == 0 ) _hash = sect().contentHash(this, ind); return _hash; } + virtual bool canCoalesceWith(const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const + { return sect().canCoalesceWith(this, rhs, ind); } + virtual ld::Fixup::iterator fixupsBegin() const { return &machofile()._fixups[_fixupsStartIndex]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &machofile()._fixups[_fixupsStartIndex+_fixupsCount]; } + virtual ld::Atom::UnwindInfo::iterator beginUnwind() const { return &machofile()._unwindInfos[_unwindInfoStartIndex]; } + virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return &machofile()._unwindInfos[_unwindInfoStartIndex+_unwindInfoCount]; } + virtual ld::Atom::LineInfo::iterator beginLineInfo() const{ return &machofile()._lineInfos[_lineInfoStartIndex]; } + virtual ld::Atom::LineInfo::iterator endLineInfo() const { return &machofile()._lineInfos[_lineInfoStartIndex+_lineInfoCount]; } + +private: + + enum { kFixupStartIndexBits = 32, + kLineInfoStartIndexBits = 32, + kUnwindInfoStartIndexBits = 24, + kFixupCountBits = 24, + kLineInfoCountBits = 12, + kUnwindInfoCountBits = 4 + }; // must sum to 128 + +public: + // methods for all atoms from mach-o object file + Section<A>& sect() const { return (Section<A>&)section(); } + File<A>& machofile() const { return ((Section<A>*)(this->_section))->file(); } + void setFixupsRange(uint32_t s, uint32_t c); + void setUnwindInfoRange(uint32_t s, uint32_t c); + void setLineInfoRange(uint32_t s, uint32_t c); + bool roomForMoreLineInfoCount() { return (_lineInfoCount < ((1<<kLineInfoCountBits)-1)); } + void incrementLineInfoCount() { assert(roomForMoreLineInfoCount()); ++_lineInfoCount; } + void incrementFixupCount() { if (_fixupsCount == ((1 << kFixupCountBits)-1)) + throwf("too may fixups in %s", name()); ++_fixupsCount; } + const uint8_t* contentPointer() const; + uint32_t fixupCount() const { return _fixupsCount; } + void verifyAlignment() const; + + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + // constuct via all attributes + Atom(Section<A>& sct, const char* nm, pint_t addr, uint64_t sz, + ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Scope s, + ld::Atom::ContentType ct, ld::Atom::SymbolTableInclusion i, + bool dds, bool thumb, bool al, ld::Atom::Alignment a) + : ld::Atom((ld::Section&)sct, d, c, s, ct, i, dds, thumb, al, a), + _size(sz), _objAddress(addr), _name(nm), _hash(0), + _fixupsStartIndex(0), _lineInfoStartIndex(0), + _unwindInfoStartIndex(0), _fixupsCount(0), + _lineInfoCount(0), _unwindInfoCount(0) { } + // construct via symbol table entry + Atom(Section<A>& sct, Parser<A>& parser, const macho_nlist<P>& sym, + uint64_t sz, bool alias=false) + : ld::Atom((ld::Section&)sct, parser.definitionFromSymbol(sym), + parser.combineFromSymbol(sym), parser.scopeFromSymbol(sym), + parser.resolverFromSymbol(sym) ? ld::Atom::typeResolver : sct.contentType(), + parser.inclusionFromSymbol(sym), + parser.dontDeadStripFromSymbol(sym) || sct.dontDeadStrip(), + parser.isThumbFromSymbol(sym), alias, + sct.alignmentForAddress(sym.n_value())), + _size(sz), _objAddress(sym.n_value()), + _name(parser.nameFromSymbol(sym)), _hash(0), + _fixupsStartIndex(0), _lineInfoStartIndex(0), + _unwindInfoStartIndex(0), _fixupsCount(0), + _lineInfoCount(0), _unwindInfoCount(0) { + // <rdar://problem/6783167> support auto-hidden weak symbols + if ( _scope == ld::Atom::scopeGlobal && + (sym.n_desc() & (N_WEAK_DEF|N_WEAK_REF)) == (N_WEAK_DEF|N_WEAK_REF) ) + this->setAutoHide(); + this->verifyAlignment(); + } + +private: + friend class Parser<A>; + friend class Section<A>; + friend class CStringSection<A>; + friend class AbsoluteSymbolSection<A>; + + pint_t _size; + pint_t _objAddress; + const char* _name; + mutable unsigned long _hash; + + uint64_t _fixupsStartIndex : kFixupStartIndexBits, + _lineInfoStartIndex : kLineInfoStartIndexBits, + _unwindInfoStartIndex : kUnwindInfoStartIndexBits, + _fixupsCount : kFixupCountBits, + _lineInfoCount : kLineInfoCountBits, + _unwindInfoCount : kUnwindInfoCountBits; + +}; + + + +template <typename A> +void Atom<A>::setFixupsRange(uint32_t startIndex, uint32_t count) +{ + if ( count >= (1 << kFixupCountBits) ) + throwf("too many fixups in function %s", this->name()); + if ( startIndex >= (1 << kFixupStartIndexBits) ) + throwf("too many fixups in file"); + assert(((startIndex+count) <= sect().file()._fixups.size()) && "fixup index out of range"); + _fixupsStartIndex = startIndex; + _fixupsCount = count; +} + +template <typename A> +void Atom<A>::setUnwindInfoRange(uint32_t startIndex, uint32_t count) +{ + if ( count >= (1 << kUnwindInfoCountBits) ) + throwf("too many compact unwind infos in function %s", this->name()); + if ( startIndex >= (1 << kUnwindInfoStartIndexBits) ) + throwf("too many compact unwind infos (%d) in file", startIndex); + assert((startIndex+count) <= sect().file()._unwindInfos.size() && "unwindinfo index out of range"); + _unwindInfoStartIndex = startIndex; + _unwindInfoCount = count; +} + +template <typename A> +void Atom<A>::setLineInfoRange(uint32_t startIndex, uint32_t count) +{ + assert((count < (1 << kLineInfoCountBits)) && "too many line infos"); + assert((startIndex+count) < sect().file()._lineInfos.size() && "line info index out of range"); + _lineInfoStartIndex = startIndex; + _lineInfoCount = count; +} + +template <typename A> +const uint8_t* Atom<A>::contentPointer() const +{ + const macho_section<P>* sct = this->sect().machoSection(); + uint32_t fileOffset = sct->offset() - sct->addr() + this->_objAddress; + return this->sect().file().fileContent()+fileOffset; +} + + +template <typename A> +void Atom<A>::copyRawContent(uint8_t buffer[]) const +{ + // copy base bytes + if ( this->contentType() == ld::Atom::typeZeroFill ) { + bzero(buffer, _size); + } + else if ( _size != 0 ) { + memcpy(buffer, this->contentPointer(), _size); + } +} + +template <> +void Atom<arm>::verifyAlignment() const +{ + if ( (this->section().type() == ld::Section::typeCode) && ! isThumb() ) { + if ( (_objAddress % 4) != 0 ) + warning("ARM function %s not 4-byte aligned", this->name()); + } +} + +template <typename A> +void Atom<A>::verifyAlignment() const +{ +} + + +template <typename A> +class Parser +{ +public: + static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, + cpu_subtype_t subtype=0); + static const char* fileKind(const uint8_t* fileContent); + static bool hasObjC2Categories(const uint8_t* fileContent); + static ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + const ParserOptions& opts) { + Parser p(fileContent, fileLength, path, modTime, + ordinal, opts.convertUnwindInfo); + return p.parse(opts); + } + + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + struct SourceLocation { + SourceLocation() {} + SourceLocation(Atom<A>* a, uint32_t o) : atom(a), offsetInAtom(o) {} + Atom<A>* atom; + uint32_t offsetInAtom; + }; + + struct TargetDesc { + Atom<A>* atom; + const char* name; // only used if targetAtom is NULL + int64_t addend; + bool weakImport; // only used if targetAtom is NULL + }; + + struct FixupInAtom { + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, Atom<A>* target) : + fixup(src.offsetInAtom, c, k, target), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, Atom<A>* target) : + fixup(src.offsetInAtom, c, k, b, target), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, bool wi, const char* name) : + fixup(src.offsetInAtom, c, k, wi, name), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, const char* name) : + fixup(src.offsetInAtom, c, k, b, name), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, uint64_t addend) : + fixup(src.offsetInAtom, c, k, addend), atom(src.atom) { src.atom->incrementFixupCount(); } + + FixupInAtom(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k) : + fixup(src.offsetInAtom, c, k, (uint64_t)0), atom(src.atom) { src.atom->incrementFixupCount(); } + + ld::Fixup fixup; + Atom<A>* atom; + }; + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, Atom<A>* target) { + _allFixups.push_back(FixupInAtom(src, c, k, target)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, Atom<A>* target) { + _allFixups.push_back(FixupInAtom(src, c, k, b, target)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, bool wi, const char* name) { + _allFixups.push_back(FixupInAtom(src, c, k, wi, name)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, ld::Fixup::TargetBinding b, const char* name) { + _allFixups.push_back(FixupInAtom(src, c, k, b, name)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k, uint64_t addend) { + _allFixups.push_back(FixupInAtom(src, c, k, addend)); + } + + void addFixup(const SourceLocation& src, ld::Fixup::Cluster c, ld::Fixup::Kind k) { + _allFixups.push_back(FixupInAtom(src, c, k)); + } + + + uint32_t symbolCount() { return _symbolCount; } + uint32_t indirectSymbol(uint32_t indirectIndex); + const macho_nlist<P>& symbolFromIndex(uint32_t index); + const char* nameFromSymbol(const macho_nlist<P>& sym); + ld::Atom::Scope scopeFromSymbol(const macho_nlist<P>& sym); + static ld::Atom::Definition definitionFromSymbol(const macho_nlist<P>& sym); + static ld::Atom::Combine combineFromSymbol(const macho_nlist<P>& sym); + ld::Atom::SymbolTableInclusion inclusionFromSymbol(const macho_nlist<P>& sym); + static bool dontDeadStripFromSymbol(const macho_nlist<P>& sym); + static bool isThumbFromSymbol(const macho_nlist<P>& sym); + static bool weakImportFromSymbol(const macho_nlist<P>& sym); + static bool resolverFromSymbol(const macho_nlist<P>& sym); + uint32_t symbolIndexFromIndirectSectionAddress(pint_t,const macho_section<P>*); + const macho_section<P>* firstMachOSection() { return _sectionsStart; } + const macho_section<P>* machOSectionFromSectionIndex(uint32_t index); + uint32_t machOSectionCount() { return _machOSectionsCount; } + uint32_t undefinedStartIndex() { return _undefinedStartIndex; } + uint32_t undefinedEndIndex() { return _undefinedEndIndex; } + void addFixup(FixupInAtom f) { _allFixups.push_back(f); } + Section<A>* sectionForNum(unsigned int sectNum); + Section<A>* sectionForAddress(pint_t addr); + Atom<A>* findAtomByAddress(pint_t addr); + Atom<A>* findAtomByAddressOrNullIfStub(pint_t addr); + Atom<A>* findAtomByAddressOrLocalTargetOfStub(pint_t addr, uint32_t* offsetInAtom); + Atom<A>* findAtomByName(const char* name); // slow! + void findTargetFromAddress(pint_t addr, TargetDesc& target); + void findTargetFromAddress(pint_t baseAddr, pint_t addr, TargetDesc& target); + void findTargetFromAddressAndSectionNum(pint_t addr, unsigned int sectNum, + TargetDesc& target); + uint32_t tentativeDefinitionCount() { return _tentativeDefinitionCount; } + uint32_t absoluteSymbolCount() { return _absoluteSymbolCount; } + + bool hasStubsSection() { return (_stubsSectionNum != 0); } + unsigned int stubsSectionNum() { return _stubsSectionNum; } + void addDtraceExtraInfos(const SourceLocation& src, const char* provider); + const char* scanSymbolTableForAddress(uint64_t addr); + bool convertUnwindInfo() { return _convertUnwindInfo; } + + + void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target); + void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase); + + + + struct LabelAndCFIBreakIterator { + typedef typename CFISection<A>::CFI_Atom_Info CFI_Atom_Info; + LabelAndCFIBreakIterator(const uint32_t* ssa, uint32_t ssc, const pint_t* cfisa, + uint32_t cfisc, bool ols) + : sortedSymbolIndexes(ssa), sortedSymbolCount(ssc), cfiStartsArray(cfisa), + cfiStartsCount(cfisc), fileHasOverlappingSymbols(ols), + newSection(false), cfiIndex(0), symIndex(0) {} + bool next(Parser<A>& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, + pint_t* addr, pint_t* size, const macho_nlist<P>** sym); + pint_t peek(Parser<A>& parser, pint_t startAddr, pint_t endAddr); + void beginSection() { newSection = true; symIndex = 0; } + + const uint32_t* const sortedSymbolIndexes; + const uint32_t sortedSymbolCount; + const pint_t* cfiStartsArray; + const uint32_t cfiStartsCount; + const bool fileHasOverlappingSymbols; + bool newSection; + uint32_t cfiIndex; + uint32_t symIndex; + }; + + struct CFIInfoArray { + typedef typename CFISection<A>::CFI_Atom_Info CFI_Atom_Info; + CFIInfoArray(const CFI_Atom_Info* cfia, uint32_t cfiac) : array(cfia), count(cfiac) {} + const CFI_Atom_Info* const array; + const uint32_t count; + }; + + +private: + friend class Section<A>; + + enum SectionType { sectionTypeIgnore, sectionTypeLiteral4, sectionTypeLiteral8, sectionTypeLiteral16, + sectionTypeNonLazy, sectionTypeCFI, sectionTypeCString, sectionTypeCStringPointer, + sectionTypeUTF16Strings, sectionTypeCFString, sectionTypeObjC2ClassRefs, typeObjC2CategoryList, + sectionTypeObjC1Classes, sectionTypeSymboled, sectionTypeObjC1ClassRefs, + sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs }; + + template <typename P> + struct MachOSectionAndSectionClass + { + const macho_section<P>* sect; + SectionType type; + + static int sorter(const void* l, const void* r) { + const MachOSectionAndSectionClass<P>* left = (MachOSectionAndSectionClass<P>*)l; + const MachOSectionAndSectionClass<P>* right = (MachOSectionAndSectionClass<P>*)r; + int64_t diff = left->sect->addr() - right->sect->addr(); + if ( diff == 0 ) + return 0; + if ( diff < 0 ) + return -1; + else + return 1; + } + }; + + Parser(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, + uint32_t ordinal, bool convertUnwindInfo); + ld::relocatable::File* parse(const ParserOptions& opts); + uint8_t loadCommandSizeMask(); + bool parseLoadCommands(); + void makeSections(); + void checkForLSDA(); + void prescanSymbolTable(); + void makeSortedSymbolsArray(uint32_t array[]); + static int pointerSorter(const void* l, const void* r); + static int symbolIndexSorter(void* extra, const void* l, const void* r); + void parseDebugInfo(); + void parseStabs(); + static bool isConstFunStabs(const char *stabStr); + bool read_comp_unit(const char ** name, const char ** comp_dir, + uint64_t *stmt_list); + const char* getDwarfString(uint64_t form, const uint8_t* p); + bool skip_form(const uint8_t ** offset, const uint8_t * end, + uint64_t form, uint8_t addr_size, bool dwarf64); + + + // filled in by constructor + const uint8_t* _fileContent; + uint32_t _fileLength; + const char* _path; + time_t _modTime; + uint32_t _ordinal; + + // filled in by parseLoadCommands() + File<A>* _file; + const macho_nlist<P>* _symbols; + uint32_t _symbolCount; + const char* _strings; + uint32_t _stringsSize; + const uint32_t* _indirectTable; + uint32_t _indirectTableCount; + uint32_t _undefinedStartIndex; + uint32_t _undefinedEndIndex; + const macho_section<P>* _sectionsStart; + uint32_t _machOSectionsCount; + bool _hasUUID; + + // filled in by parse() + CFISection<A>* _EHFrameSection; + AbsoluteSymbolSection<A>* _absoluteSection; + uint32_t _lsdaTextSectionNum; + uint32_t _lsdaDataSectionNum; + uint32_t _tentativeDefinitionCount; + uint32_t _absoluteSymbolCount; + uint32_t _symbolsInSections; + bool _hasLongBranchStubs; + bool _AppleObjc; // FSF has objc that uses different data layout + bool _overlappingSymbols; + bool _convertUnwindInfo; + unsigned int _stubsSectionNum; + const macho_section<P>* _stubsMachOSection; + std::vector<const char*> _dtraceProviderInfo; + std::vector<FixupInAtom> _allFixups; +}; + + + +template <typename A> +Parser<A>::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, + uint32_t ordinal, bool convertDUI) + : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), + _ordinal(ordinal), _file(NULL), + _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), + _indirectTable(NULL), _indirectTableCount(0), + _undefinedStartIndex(0), _undefinedEndIndex(0), + _sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false), + _EHFrameSection(NULL), _absoluteSection(NULL), + _lsdaTextSectionNum(0), _lsdaDataSectionNum(0), + _tentativeDefinitionCount(0), _absoluteSymbolCount(0), + _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), + _overlappingSymbols(false), _convertUnwindInfo(convertDUI), + _stubsSectionNum(0), _stubsMachOSection(NULL) +{ +} + +template <> +bool Parser<ppc>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser<ppc64>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser<x86>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_I386 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser<x86_64>::validFile(const uint8_t* fileContent, bool, cpu_subtype_t) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + +template <> +bool Parser<arm>::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return false; + if ( header->cputype() != CPU_TYPE_ARM ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + if ( subtypeMustMatch ) { + if ( (cpu_subtype_t)header->cpusubtype() == subtype ) + return true; + // hack until libcc_kext.a is made fat + if ( header->cpusubtype() == CPU_SUBTYPE_ARM_ALL ) + return true; + return false; + } + return true; +} + + +template <> +const char* Parser<ppc>::fileKind(const uint8_t* fileContent) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return NULL; + switch ( header->cpusubtype() ) { + case CPU_SUBTYPE_POWERPC_750: + return "ppc750"; + case CPU_SUBTYPE_POWERPC_7400: + return "ppc7400"; + case CPU_SUBTYPE_POWERPC_7450: + return "ppc7450"; + case CPU_SUBTYPE_POWERPC_970: + return "ppc970"; + case CPU_SUBTYPE_POWERPC_ALL: + return "ppc"; + } + return "ppc???"; +} + +template <> +const char* Parser<ppc64>::fileKind(const uint8_t* fileContent) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return NULL; + return "ppc64"; +} + +template <> +const char* Parser<x86>::fileKind(const uint8_t* fileContent) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_I386 ) + return NULL; + return "i386"; +} + +template <> +const char* Parser<x86_64>::fileKind(const uint8_t* fileContent) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return NULL; + return "x86_64"; +} + +template <> +const char* Parser<arm>::fileKind(const uint8_t* fileContent) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM ) + return NULL; + switch ( header->cpusubtype() ) { + case CPU_SUBTYPE_ARM_V4T: + return "armv4t"; + case CPU_SUBTYPE_ARM_V5TEJ: + return "armv5"; + case CPU_SUBTYPE_ARM_V6: + return "armv6"; + case CPU_SUBTYPE_ARM_V7: + return "armv7"; + case CPU_SUBTYPE_ARM_ALL: + return "arm-ALL"; + } + return "arm???"; +} + + +template <typename A> +bool Parser<A>::hasObjC2Categories(const uint8_t* fileContent) +{ + const macho_header<P>* header = (const macho_header<P>*)fileContent; + const uint32_t cmd_count = header->ncmds(); + const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>)); + const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds()); + const macho_load_command<P>* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command<P>::CMD ) { + const macho_segment_command<P>* segment = (macho_segment_command<P>*)cmd; + const macho_section<P>* sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>)); + for (uint32_t si=0; si < segment->nsects(); ++si) { + const macho_section<P>* sect = §ionsStart[si]; + if ( (sect->size() > 0) + && (strcmp(sect->sectname(), "__objc_catlist") == 0) + && (strcmp(sect->segname(), "__DATA") == 0) ) { + return true; + } + } + } + cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + return false; +} + +template <typename A> +int Parser<A>::pointerSorter(const void* l, const void* r) +{ + // sort references by address + const pint_t* left = (pint_t*)l; + const pint_t* right = (pint_t*)r; + return (*left - *right); +} + +template <typename A> +typename A::P::uint_t Parser<A>::LabelAndCFIBreakIterator::peek(Parser<A>& parser, pint_t startAddr, pint_t endAddr) +{ + pint_t symbolAddr; + if ( symIndex < sortedSymbolCount ) + symbolAddr = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]).n_value(); + else + symbolAddr = endAddr; + pint_t cfiAddr; + if ( cfiIndex < cfiStartsCount ) + cfiAddr = cfiStartsArray[cfiIndex]; + else + cfiAddr = endAddr; + if ( (cfiAddr < symbolAddr) && (cfiAddr >= startAddr) ) { + if ( cfiAddr < endAddr ) + return cfiAddr; + else + return endAddr; + } + else { + if ( symbolAddr < endAddr ) + return symbolAddr; + else + return endAddr; + } +} + +// +// Parses up a section into chunks based on labels and CFI information. +// Each call returns the next chunk address and size, and (if the break +// was becuase of a label, the symbol). Returns false when no more chunks. +// +template <typename A> +bool Parser<A>::LabelAndCFIBreakIterator::next(Parser<A>& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, + pint_t* addr, pint_t* size, const macho_nlist<P>** symbol) +{ + // may not be a label on start of section, but need atom demarcation there + if ( newSection ) { + newSection = false; + // advance symIndex until we get to the first label at or past the start of this section + while ( symIndex < sortedSymbolCount ) { + const macho_nlist<P>& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + if ( (nextSymbolAddr >= startAddr) && (sym.n_sect() >= sectNum) ) + break; + ++symIndex; + } + if ( symIndex < sortedSymbolCount ) { + const macho_nlist<P>& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + // if next symbol found is not in this section + if ( sym.n_sect() != sectNum ) { + // check for CFI break instead of symbol break + if ( cfiIndex < cfiStartsCount ) { + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextCfiAddr < endAddr ) { + // use cfi + ++cfiIndex; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + } + *addr = startAddr; + *size = endAddr - startAddr; + *symbol = NULL; + if ( startAddr == endAddr ) + return false; // zero size section + else + return true; // whole section is one atom with no label + } + // if also CFI break here, eat it + if ( cfiIndex < cfiStartsCount ) { + if ( cfiStartsArray[cfiIndex] == nextSymbolAddr ) + ++cfiIndex; + } + if ( nextSymbolAddr == startAddr ) { + // label at start of section, return it as chunk + ++symIndex; + *addr = startAddr; + *size = peek(parser, startAddr, endAddr) - startAddr; + *symbol = &sym; + return true; + } + // return chunk before first symbol + *addr = startAddr; + *size = nextSymbolAddr - startAddr; + *symbol = NULL; + return true; + } + // no symbols left in whole file, so entire section is one chunk + *addr = startAddr; + *size = endAddr - startAddr; + *symbol = NULL; + if ( startAddr == endAddr ) + return false; // zero size section + else + return true; // whole section is one atom with no label + } + + while ( (symIndex < sortedSymbolCount) && (cfiIndex < cfiStartsCount) ) { + const macho_nlist<P>& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextSymbolAddr < nextCfiAddr ) { + if ( nextSymbolAddr >= endAddr ) + return false; + ++symIndex; + if ( nextSymbolAddr < startAddr ) + continue; + *addr = nextSymbolAddr; + *size = peek(parser, startAddr, endAddr) - nextSymbolAddr; + *symbol = &sym; + return true; + } + else if ( nextCfiAddr < nextSymbolAddr ) { + if ( nextCfiAddr >= endAddr ) + return false; + ++cfiIndex; + if ( nextCfiAddr < startAddr ) + continue; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + else { + if ( nextCfiAddr >= endAddr ) + return false; + ++symIndex; + ++cfiIndex; + if ( nextCfiAddr < startAddr ) + continue; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = &sym; + return true; + } + } + while ( symIndex < sortedSymbolCount ) { + const macho_nlist<P>& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); + pint_t nextSymbolAddr = sym.n_value(); + // if next symbol found is not in this section, then done with iteration + if ( sym.n_sect() != sectNum ) + return false; + ++symIndex; + if ( nextSymbolAddr < startAddr ) + continue; + *addr = nextSymbolAddr; + *size = peek(parser, startAddr, endAddr) - nextSymbolAddr; + *symbol = &sym; + return true; + } + while ( cfiIndex < cfiStartsCount ) { + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextCfiAddr >= endAddr ) + return false; + ++cfiIndex; + if ( nextCfiAddr < startAddr ) + continue; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + return false; +} + + + +template <typename A> +ld::relocatable::File* Parser<A>::parse(const ParserOptions& opts) +{ + // create file object + _file = new File<A>(_path, _modTime, _fileContent, _ordinal); + + // respond to -t option + if ( opts.logAllFiles ) + printf("%s\n", _path); + + // parse start of mach-o file + if ( ! parseLoadCommands() ) + return _file; + + // make symbol table sorted by address + this->checkForLSDA(); + this->prescanSymbolTable(); + uint32_t sortedSymbolIndexes[_symbolsInSections]; + this->makeSortedSymbolsArray(sortedSymbolIndexes); + + // allocate Section<A> object for each mach-o section + makeSections(); + + // if it exists, do special parsing of __eh_frame section + // stack allocate array of CFI_Atom_Info + uint32_t countOfCFIs = 0; + if ( _EHFrameSection != NULL ) + countOfCFIs = _EHFrameSection->cfiCount(); + typename CFISection<A>::CFI_Atom_Info cfiArray[countOfCFIs]; + // stack allocate (if not too large) a copy of __eh_frame to apply relocations to + uint8_t* ehBuffer = NULL; + uint32_t stackAllocSize = 0; + if ( (countOfCFIs != 0) && _EHFrameSection->needsRelocating() ) { + uint32_t sectSize = _EHFrameSection->machoSection()->size(); + if ( sectSize > 50*1024 ) + ehBuffer = (uint8_t*)malloc(sectSize); + else + stackAllocSize = sectSize; + } + uint32_t ehStackBuffer[1+stackAllocSize/4]; // make 4-byte aligned stack bufffer + if ( ehBuffer == NULL ) + ehBuffer = (uint8_t*)&ehStackBuffer; + uint32_t cfiStartsCount = 0; + if ( countOfCFIs != 0 ) { + _EHFrameSection->cfiParse(*this, ehBuffer, cfiArray, countOfCFIs); + // count functions and lsdas + for(uint32_t i=0; i < countOfCFIs; ++i) { + if ( cfiArray[i].isCIE ) + continue; + //fprintf(stderr, "cfiArray[i].func = 0x%08llX, cfiArray[i].lsda = 0x%08llX, encoding=0x%08X\n", + // (uint64_t)cfiArray[i].u.fdeInfo.function.targetAddress, + // (uint64_t)cfiArray[i].u.fdeInfo.lsda.targetAddress, + // cfiArray[i].u.fdeInfo.compactUnwindInfo); + if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) + ++cfiStartsCount; + if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) + ++cfiStartsCount; + } + } + CFIInfoArray cfis(cfiArray, countOfCFIs); + + // create sorted array of function starts and lsda starts + pint_t cfiStartsArray[cfiStartsCount]; + uint32_t countOfFDEs = 0; + if ( countOfCFIs != 0 ) { + int index = 0; + for(uint32_t i=0; i < countOfCFIs; ++i) { + if ( cfiArray[i].isCIE ) + continue; + if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) + cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.function.targetAddress; + if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) + cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; + ++countOfFDEs; + } + ::qsort(cfiStartsArray, cfiStartsCount, sizeof(pint_t), pointerSorter); + #ifndef NDEBUG + // scan for FDEs claming the same function + for(int i=1; i < index; ++i) { + assert( cfiStartsArray[i] != cfiStartsArray[i-1] ); + } + #endif + } + + Section<A>** sections = _file->_sectionsArray; + uint32_t sectionsCount = _file->_sectionsArrayCount; + + // figure out how many atoms will be allocated and allocate + LabelAndCFIBreakIterator breakIterator(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, + cfiStartsCount, _overlappingSymbols); + uint32_t computedAtomCount = 0; + for (uint32_t i=0; i < sectionsCount; ++i ) { + breakIterator.beginSection(); + uint32_t count = sections[i]->computeAtomCount(*this, breakIterator, cfis); + //const macho_section<P>* sect = sections[i]->machoSection(); + //fprintf(stderr, "computed count=%u for section %s size=%llu\n", count, sect->sectname(), (sect != NULL) ? sect->size() : 0); + computedAtomCount += count; + } + //fprintf(stderr, "allocating %d atoms * sizeof(Atom<A>)=%ld, sizeof(ld::Atom)=%ld\n", computedAtomCount, sizeof(Atom<A>), sizeof(ld::Atom)); + _file->_atomsArray = new uint8_t[computedAtomCount*sizeof(Atom<A>)]; + _file->_atomsArrayCount = 0; + + // have each section append atoms to _atomsArray + LabelAndCFIBreakIterator breakIterator2(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, + cfiStartsCount, _overlappingSymbols); + for (uint32_t i=0; i < sectionsCount; ++i ) { + uint8_t* atoms = _file->_atomsArray + _file->_atomsArrayCount*sizeof(Atom<A>); + breakIterator2.beginSection(); + uint32_t count = sections[i]->appendAtoms(*this, atoms, breakIterator2, cfis); + //fprintf(stderr, "append count=%u for section %s\n", count, sections[i]->machoSection()->sectname()); + _file->_atomsArrayCount += count; + } + assert( _file->_atomsArrayCount == computedAtomCount && "more atoms allocated than expected"); + + + // have each section add all fix-ups for its atoms + _allFixups.reserve(computedAtomCount*5); + for (uint32_t i=0; i < sectionsCount; ++i ) + sections[i]->makeFixups(*this, cfis); + + // assign fixups start offset for each atom + uint8_t* p = _file->_atomsArray; + uint32_t fixupOffset = 0; + for(int i=_file->_atomsArrayCount; i > 0; --i) { + Atom<A>* atom = (Atom<A>*)p; + atom->_fixupsStartIndex = fixupOffset; + fixupOffset += atom->_fixupsCount; + atom->_fixupsCount = 0; + p += sizeof(Atom<A>); + } + assert(fixupOffset == _allFixups.size()); + _file->_fixups.reserve(fixupOffset); + + // copy each fixup for each atom + for(typename std::vector<FixupInAtom>::iterator it=_allFixups.begin(); it != _allFixups.end(); ++it) { + uint32_t slot = it->atom->_fixupsStartIndex + it->atom->_fixupsCount; + _file->_fixups[slot] = it->fixup; + it->atom->_fixupsCount++; + } + + // done with temp vector + _allFixups.clear(); + + // add unwind info + _file->_unwindInfos.reserve(countOfFDEs); + for(uint32_t i=0; i < countOfCFIs; ++i) { + if ( cfiArray[i].isCIE ) + continue; + if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) { + ld::Atom::UnwindInfo info; + info.startOffset = 0; + info.unwindInfo = cfiArray[i].u.fdeInfo.compactUnwindInfo; + _file->_unwindInfos.push_back(info); + Atom<A>* func = findAtomByAddress(cfiArray[i].u.fdeInfo.function.targetAddress); + func->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); + } + } + + // parse dwarf debug info to get line info + this->parseDebugInfo(); + + return _file; +} + + + +template <> uint8_t Parser<ppc>::loadCommandSizeMask() { return 0x03; } +template <> uint8_t Parser<ppc64>::loadCommandSizeMask() { return 0x07; } +template <> uint8_t Parser<x86>::loadCommandSizeMask() { return 0x03; } +template <> uint8_t Parser<x86_64>::loadCommandSizeMask() { return 0x07; } +template <> uint8_t Parser<arm>::loadCommandSizeMask() { return 0x03; } + +template <typename A> +bool Parser<A>::parseLoadCommands() +{ + const macho_header<P>* header = (const macho_header<P>*)_fileContent; + + // set File attributes + _file->_canScatterAtoms = (header->flags() & MH_SUBSECTIONS_VIA_SYMBOLS); + _file->_cpuSubType = header->cpusubtype(); + + const macho_segment_command<P>* segment = NULL; + const uint8_t* const endOfFile = _fileContent + _fileLength; + const uint32_t cmd_count = header->ncmds(); + // <rdar://problem/5394172> an empty .o file with zero load commands will crash linker + if ( cmd_count == 0 ) + return false; + const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>)); + const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds()); + const macho_load_command<P>* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + if ( (size & this->loadCommandSizeMask()) != 0 ) + throwf("load command #%d has a unaligned size", i); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > (uint8_t*)cmdsEnd ) + throwf("load command #%d extends beyond the end of the load commands", i); + if ( endOfCmd > endOfFile ) + throwf("load command #%d extends beyond the end of the file", i); + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd; + _symbolCount = symtab->nsyms(); + _symbols = (const macho_nlist<P>*)(_fileContent + symtab->symoff()); + _strings = (char*)_fileContent + symtab->stroff(); + _stringsSize = symtab->strsize(); + if ( (symtab->symoff() + _symbolCount*sizeof(macho_nlist<P>)) > _fileLength ) + throw "mach-o symbol table extends beyond end of file"; + if ( (_strings + _stringsSize) > (char*)endOfFile ) + throw "mach-o string pool extends beyond end of file"; + if ( _indirectTable == NULL ) { + if ( _undefinedEndIndex == 0 ) { + _undefinedStartIndex = 0; + _undefinedEndIndex = symtab->nsyms(); + } + } + } + break; + case LC_DYSYMTAB: + { + const macho_dysymtab_command<P>* dsymtab = (macho_dysymtab_command<P>*)cmd; + _indirectTable = (uint32_t*)(_fileContent + dsymtab->indirectsymoff()); + _indirectTableCount = dsymtab->nindirectsyms(); + if ( &_indirectTable[_indirectTableCount] > (uint32_t*)endOfFile ) + throw "indirect symbol table extends beyond end of file"; + _undefinedStartIndex = dsymtab->iundefsym(); + _undefinedEndIndex = _undefinedStartIndex + dsymtab->nundefsym(); + } + break; + case LC_UUID: + _hasUUID = true; + break; + + default: + if ( cmd->cmd() == macho_segment_command<P>::CMD ) { + if ( segment != NULL ) + throw "more than one LC_SEGMENT found in object file"; + segment = (macho_segment_command<P>*)cmd; + } + break; + } + cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + + // record range of sections + if ( segment == NULL ) + throw "missing LC_SEGMENT"; + _sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>)); + _machOSectionsCount = segment->nsects(); + + return true; +} + +template <> +void Parser<arm>::checkForLSDA() +{ + // ARM has no FDEs, so need labels to break up section into atoms +} + +template <typename A> +void Parser<A>::checkForLSDA() +{ + // ignore labels on __gcc_except_tab section, we'll break it into atoms based on FDE info + for (uint32_t i=0; i < _machOSectionsCount; ++i) { + const macho_section<P>* sect = &_sectionsStart[i]; + if ( strncmp(sect->sectname(), "__gcc_except_tab", 16) == 0 ) { + if ( strcmp(sect->segname(), "__TEXT") == 0 ) { + assert(_lsdaTextSectionNum == 0); + _lsdaTextSectionNum = i+1; + } + else if ( strcmp(sect->segname(), "__DATA") == 0 ) { + assert(_lsdaDataSectionNum == 0); + _lsdaDataSectionNum = i+1; + } + } + } +} + + +template <typename A> +void Parser<A>::prescanSymbolTable() +{ + _tentativeDefinitionCount = 0; + _absoluteSymbolCount = 0; + _symbolsInSections = 0; + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist<P>& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // look at undefines + const char* symbolName = this->nameFromSymbol(sym); + if ( (sym.n_type() & N_TYPE) == N_UNDF ) { + if ( sym.n_value() != 0 ) { + // count tentative definitions + ++_tentativeDefinitionCount; + } + else if ( strncmp(symbolName, "___dtrace_", 10) == 0 ) { + // any undefined starting with __dtrace_*$ that is not ___dtrace_probe$* or ___dtrace_isenabled$* + // is extra provider info + if ( (strncmp(&symbolName[10], "probe$", 6) != 0) && (strncmp(&symbolName[10], "isenabled$", 10) != 0) ) { + _dtraceProviderInfo.push_back(symbolName); + } + } + continue; + } + + // count absolute symbols + if ( (sym.n_type() & N_TYPE) == N_ABS ) { + const char* absName = this->nameFromSymbol(sym); + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_class_name_", 17) == 0 ) { + _AppleObjc = true; + continue; + } + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_category_name_", 20) == 0 ) + continue; + // ignore empty *.eh symbols + if ( strcmp(&absName[strlen(absName)-3], ".eh") == 0 ) + continue; + ++_absoluteSymbolCount; + } + + // only look at definitions + if ( (sym.n_type() & N_TYPE) != N_SECT ) + continue; + + // 'L' labels do not denote atom breaks + if ( symbolName[0] == 'L' ) + continue; + + // ignore labels in __gcc_except_tab section + if ( (_lsdaTextSectionNum != 0) && (sym.n_sect() == _lsdaTextSectionNum) ) + continue; + if ( (_lsdaDataSectionNum != 0) && (sym.n_sect() == _lsdaDataSectionNum) ) + continue; + + // how many def syms in each section + if ( sym.n_sect() > _machOSectionsCount ) + throw "bad n_sect in symbol table"; + + _symbolsInSections++; + } +} + +template <typename A> +int Parser<A>::symbolIndexSorter(void* extra, const void* l, const void* r) +{ + Parser<A>* parser = (Parser<A>*)extra; + const uint32_t* left = (uint32_t*)l; + const uint32_t* right = (uint32_t*)r; + const macho_nlist<P>& leftSym = parser->symbolFromIndex(*left); + const macho_nlist<P>& rightSym = parser->symbolFromIndex(*right); + // can't just return difference because 64-bit diff does not fit in 32-bit return type + int64_t result = leftSym.n_value() - rightSym.n_value(); + if ( result == 0 ) { + // two symbols with same address + // if in different sections, sort earlier section first + if ( leftSym.n_sect() != rightSym.n_sect() ) + return (leftSym.n_sect() - rightSym.n_sect()); + //, means one is an alias + // if only one is global, make the other an alias (sort first) + if ( (leftSym.n_type() & N_EXT) != (rightSym.n_type() & N_EXT) ) { + if ( (rightSym.n_type() & N_EXT) != 0 ) + return -1; + else + return 1; + } + // if both are global, make alphabetically last one be the alias + return ( strcmp(parser->nameFromSymbol(rightSym), parser->nameFromSymbol(leftSym)) ); + } + else if ( result < 0 ) + return -1; + else + return 1; +} + +template <typename A> +void Parser<A>::makeSortedSymbolsArray(uint32_t array[]) +{ + uint32_t* p = array; + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist<P>& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // only look at definitions + if ( (sym.n_type() & N_TYPE) != N_SECT ) + continue; + + // 'L' labels do not denote atom breaks + const char* symbolName = this->nameFromSymbol(sym); + if ( symbolName[0] == 'L' ) + continue; + + // ignore labels in __gcc_except_tab section + if ( (_lsdaTextSectionNum != 0) && (sym.n_sect() == _lsdaTextSectionNum) ) + continue; + if ( (_lsdaDataSectionNum != 0) && (sym.n_sect() == _lsdaDataSectionNum) ) + continue; + + // how many def syms in each section + if ( sym.n_sect() > _machOSectionsCount ) + throw "bad n_sect in symbol table"; + + // append to array + *p++ = i; + } + assert(p == &array[_symbolsInSections] && "second pass over symbol table yield a different number of symbols"); + + // sort by symbol table address + ::qsort_r(array, _symbolsInSections, sizeof(uint32_t), this, &symbolIndexSorter); + + // look for two symbols at same address + _overlappingSymbols = false; + for (unsigned int i=1; i < _symbolsInSections; ++i) { + if ( symbolFromIndex(array[i-1]).n_value() == symbolFromIndex(array[i]).n_value() ) { + //fprintf(stderr, "overlapping symbols at 0x%08llX\n", symbolFromIndex(array[i-1]).n_value()); + _overlappingSymbols = true; + } + } + + //fprintf(stderr, "sorted symbols:\n"); + //for(unsigned int i=0; i < _symbolsInSections; ++i ) + // fprintf(stderr, "0x%09llX symIndex=%3d sectNum=%2d, %s\n", symbolFromIndex(array[i]).n_value(), array[i], symbolFromIndex(array[i]).n_sect(), nameFromSymbol(symbolFromIndex(array[i])) ); +} + + +template <typename A> +void Parser<A>::makeSections() +{ + // classify each section by type + // compute how many Section objects will be needed and total size for all + unsigned int totalSectionsSize = 0; + uint8_t machOSectsStorage[sizeof(MachOSectionAndSectionClass<P>)*(_machOSectionsCount+2)]; // also room for tentative-defs and absolute symbols + // allocate raw storage for all section objects on stack + MachOSectionAndSectionClass<P>* machOSects = (MachOSectionAndSectionClass<P>*)machOSectsStorage; + unsigned int count = 0; + for (uint32_t i=0; i < _machOSectionsCount; ++i) { + const macho_section<P>* sect = &_sectionsStart[i]; + // ignore dwarf sections + if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { + // note that .o file has dwarf + _file->_debugInfoKind = ld::relocatable::File::kDebugInfoDwarf; + // save off iteresting dwarf sections + if ( strcmp(sect->sectname(), "__debug_info") == 0 ) + _file->_dwarfDebugInfoSect = sect; + else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 ) + _file->_dwarfDebugAbbrevSect = sect; + else if ( strcmp(sect->sectname(), "__debug_line") == 0 ) + _file->_dwarfDebugLineSect = sect; + else if ( strcmp(sect->sectname(), "__debug_str") == 0 ) + _file->_dwarfDebugStringSect = sect; + // linker does not propagate dwarf sections to output file + continue; + } + // ignore empty __OBJC sections + if ( (sect->size() == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) + continue; + // objc image info section is really attributes and not content + if ( ((strcmp(sect->sectname(), "__image_info") == 0) && (strcmp(sect->segname(), "__OBJC") == 0)) + || ((strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0)) ) { + // struct objc_image_info { + // uint32_t version; // initially 0 + // uint32_t flags; + // }; + // #define OBJC_IMAGE_SUPPORTS_GC 2 + // #define OBJC_IMAGE_GC_ONLY 4 + // + const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset()); + if ( (sect->size() >= 8) && (contents[0] == 0) ) { + uint32_t flags = E::get32(contents[1]); + if ( (flags & 4) == 4 ) + _file->_objConstraint = ld::File::objcConstraintGC; + else if ( (flags & 2) == 2 ) + _file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else + _file->_objConstraint = ld::File::objcConstraintRetainRelease; + if ( (flags & 1) == 1 ) + _file->_ojcReplacmentClass = true; + if ( sect->size() > 8 ) { + warning("section %s/%s has unexpectedly large size %llu in %s", + sect->segname(), sect->sectname(), sect->size(), _file->path()); + } + } + else { + warning("can't parse %s/%s section in %s", sect->segname(), sect->sectname(), _file->path()); + } + continue; + } + machOSects[count].sect = sect; + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + if ( _stubsSectionNum == 0 ) { + _stubsSectionNum = i+1; + _stubsMachOSection = sect; + } + else + assert(1 && "multiple S_SYMBOL_STUBS sections"); + case S_LAZY_SYMBOL_POINTERS: + break; + case S_4BYTE_LITERALS: + totalSectionsSize += sizeof(Literal4Section<A>); + machOSects[count++].type = sectionTypeLiteral4; + break; + case S_8BYTE_LITERALS: + totalSectionsSize += sizeof(Literal8Section<A>); + machOSects[count++].type = sectionTypeLiteral8; + break; + case S_16BYTE_LITERALS: + totalSectionsSize += sizeof(Literal16Section<A>); + machOSects[count++].type = sectionTypeLiteral16; + break; + case S_NON_LAZY_SYMBOL_POINTERS: + totalSectionsSize += sizeof(NonLazyPointerSection<A>); + machOSects[count++].type = sectionTypeNonLazy; + break; + case S_LITERAL_POINTERS: + if ( (strcmp(sect->segname(), "__OBJC") == 0) && (strcmp(sect->sectname(), "__cls_refs") == 0) ) { + totalSectionsSize += sizeof(Objc1ClassReferences<A>); + machOSects[count++].type = sectionTypeObjC1ClassRefs; + } + else { + totalSectionsSize += sizeof(PointerToCStringSection<A>); + machOSects[count++].type = sectionTypeCStringPointer; + } + break; + case S_CSTRING_LITERALS: + totalSectionsSize += sizeof(CStringSection<A>); + machOSects[count++].type = sectionTypeCString; + break; + case S_MOD_INIT_FUNC_POINTERS: + case S_MOD_TERM_FUNC_POINTERS: + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + case S_INTERPOSING: + case S_ZEROFILL: + case S_REGULAR: + case S_COALESCED: + case S_THREAD_LOCAL_REGULAR: + case S_THREAD_LOCAL_ZEROFILL: + if ( (strcmp(sect->segname(), "__TEXT") == 0) && (strcmp(sect->sectname(), "__eh_frame") == 0) ) { + totalSectionsSize += sizeof(CFISection<A>); + machOSects[count++].type = sectionTypeCFI; + } + else if ( (strcmp(sect->segname(), "__DATA") == 0) && (strcmp(sect->sectname(), "__cfstring") == 0) ) { + totalSectionsSize += sizeof(CFStringSection<A>); + machOSects[count++].type = sectionTypeCFString; + } + else if ( (strcmp(sect->segname(), "__TEXT") == 0) && (strcmp(sect->sectname(), "__ustring") == 0) ) { + totalSectionsSize += sizeof(UTF16StringSection<A>); + machOSects[count++].type = sectionTypeUTF16Strings; + } + else if ( (strcmp(sect->segname(), "__DATA") == 0) && (strncmp(sect->sectname(), "__objc_classrefs", 16) == 0) ) { + totalSectionsSize += sizeof(ObjC2ClassRefsSection<A>); + machOSects[count++].type = sectionTypeObjC2ClassRefs; + } + else if ( (strcmp(sect->segname(), "__DATA") == 0) && (strcmp(sect->sectname(), "__objc_catlist") == 0) ) { + totalSectionsSize += sizeof(ObjC2CategoryListSection<A>); + machOSects[count++].type = typeObjC2CategoryList; + } + else if ( _AppleObjc && (strcmp(sect->segname(), "__OBJC") == 0) && (strcmp(sect->sectname(), "__class") == 0) ) { + totalSectionsSize += sizeof(ObjC1ClassSection<A>); + machOSects[count++].type = sectionTypeObjC1Classes; + } + else { + totalSectionsSize += sizeof(SymboledSection<A>); + machOSects[count++].type = sectionTypeSymboled; + } + break; + case S_THREAD_LOCAL_VARIABLES: + totalSectionsSize += sizeof(TLVDefsSection<A>); + machOSects[count++].type = sectionTypeTLVDefs; + break; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + default: + throwf("unknown section type %d", sect->flags() & SECTION_TYPE); + } + } + + // sort by address (mach-o object files don't aways have sections sorted) + ::qsort(machOSects, count, sizeof(MachOSectionAndSectionClass<P>), MachOSectionAndSectionClass<P>::sorter); + + // we will synthesize a dummy Section<A> object for tentative definitions + if ( _tentativeDefinitionCount > 0 ) { + totalSectionsSize += sizeof(TentativeDefinitionSection<A>); + machOSects[count++].type = sectionTypeTentativeDefinitions; + } + + // we will synthesize a dummy Section<A> object for Absolute symbols + if ( _absoluteSymbolCount > 0 ) { + totalSectionsSize += sizeof(AbsoluteSymbolSection<A>); + machOSects[count++].type = sectionTypeAbsoluteSymbols; + } + + // allocate one block for all Section objects as well as pointers to each + uint8_t* space = new uint8_t[totalSectionsSize+count*sizeof(Section<A>*)]; + _file->_sectionsArray = (Section<A>**)space; + _file->_sectionsArrayCount = count; + Section<A>** objects = _file->_sectionsArray; + space += count*sizeof(Section<A>*); + for (uint32_t i=0; i < count; ++i) { + switch ( machOSects[i].type ) { + case sectionTypeIgnore: + break; + case sectionTypeLiteral4: + *objects++ = new (space) Literal4Section<A>(*this, *_file, machOSects[i].sect); + space += sizeof(Literal4Section<A>); + break; + case sectionTypeLiteral8: + *objects++ = new (space) Literal8Section<A>(*this, *_file, machOSects[i].sect); + space += sizeof(Literal8Section<A>); + break; + case sectionTypeLiteral16: + *objects++ = new (space) Literal16Section<A>(*this, *_file, machOSects[i].sect); + space += sizeof(Literal16Section<A>); + break; + case sectionTypeNonLazy: + *objects++ = new (space) NonLazyPointerSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(NonLazyPointerSection<A>); + break; + case sectionTypeCFI: + _EHFrameSection = new (space) CFISection<A>(*this, *_file, machOSects[i].sect); + *objects++ = _EHFrameSection; + space += sizeof(CFISection<A>); + break; + case sectionTypeCString: + *objects++ = new (space) CStringSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(CStringSection<A>); + break; + case sectionTypeCStringPointer: + *objects++ = new (space) PointerToCStringSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(PointerToCStringSection<A>); + break; + case sectionTypeObjC1ClassRefs: + *objects++ = new (space) Objc1ClassReferences<A>(*this, *_file, machOSects[i].sect); + space += sizeof(Objc1ClassReferences<A>); + break; + case sectionTypeUTF16Strings: + *objects++ = new (space) UTF16StringSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(UTF16StringSection<A>); + break; + case sectionTypeCFString: + *objects++ = new (space) CFStringSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(CFStringSection<A>); + break; + case sectionTypeObjC2ClassRefs: + *objects++ = new (space) ObjC2ClassRefsSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(ObjC2ClassRefsSection<A>); + break; + case typeObjC2CategoryList: + *objects++ = new (space) ObjC2CategoryListSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(ObjC2CategoryListSection<A>); + break; + case sectionTypeObjC1Classes: + *objects++ = new (space) ObjC1ClassSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(ObjC1ClassSection<A>); + break; + case sectionTypeSymboled: + *objects++ = new (space) SymboledSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(SymboledSection<A>); + break; + case sectionTypeTLVDefs: + *objects++ = new (space) TLVDefsSection<A>(*this, *_file, machOSects[i].sect); + space += sizeof(TLVDefsSection<A>); + break; + case sectionTypeTentativeDefinitions: + *objects++ = new (space) TentativeDefinitionSection<A>(*this, *_file); + space += sizeof(TentativeDefinitionSection<A>); + break; + case sectionTypeAbsoluteSymbols: + _absoluteSection = new (space) AbsoluteSymbolSection<A>(*this, *_file); + *objects++ = _absoluteSection; + space += sizeof(AbsoluteSymbolSection<A>); + break; + default: + throw "internal error uknown SectionType"; + } + } +} + + +template <typename A> +Section<A>* Parser<A>::sectionForAddress(typename A::P::uint_t addr) +{ + for (uint32_t i=0; i < _file->_sectionsArrayCount; ++i ) { + const macho_section<typename A::P>* sect = _file->_sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { + return _file->_sectionsArray[i]; + } + } + } + // not strictly in any section + // may be in a zero length section + for (uint32_t i=0; i < _file->_sectionsArrayCount; ++i ) { + const macho_section<typename A::P>* sect = _file->_sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( (sect->addr() == addr) && (sect->size() == 0) ) { + return _file->_sectionsArray[i]; + } + } + } + + throwf("sectionForAddress(0x%llX) address not in any section", (uint64_t)addr); +} + +template <typename A> +Section<A>* Parser<A>::sectionForNum(unsigned int num) +{ + for (uint32_t i=0; i < _file->_sectionsArrayCount; ++i ) { + const macho_section<typename A::P>* sect = _file->_sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( num == (unsigned int)((sect - _sectionsStart)+1) ) + return _file->_sectionsArray[i]; + } + } + throwf("sectionForNum(%u) section number not for any section", num); +} + +template <typename A> +Atom<A>* Parser<A>::findAtomByAddress(pint_t addr) +{ + Section<A>* section = this->sectionForAddress(addr); + return section->findAtomByAddress(addr); +} + +template <typename A> +Atom<A>* Parser<A>::findAtomByAddressOrNullIfStub(pint_t addr) +{ + if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) + return NULL; + return findAtomByAddress(addr); +} + +template <typename A> +Atom<A>* Parser<A>::findAtomByAddressOrLocalTargetOfStub(pint_t addr, uint32_t* offsetInAtom) +{ + if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) { + // target is a stub, remove indirection + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); + const macho_nlist<P>& sym = this->symbolFromIndex(symbolIndex); + // can't be to external weak symbol + assert( (this->combineFromSymbol(sym) != ld::Atom::combineByName) || (this->scopeFromSymbol(sym) != ld::Atom::scopeGlobal) ); + *offsetInAtom = 0; + return this->findAtomByName(this->nameFromSymbol(sym)); + } + Atom<A>* target = this->findAtomByAddress(addr); + *offsetInAtom = addr - target->_objAddress; + return target; +} + +template <typename A> +Atom<A>* Parser<A>::findAtomByName(const char* name) +{ + uint8_t* p = _file->_atomsArray; + for(int i=_file->_atomsArrayCount; i > 0; --i) { + Atom<A>* atom = (Atom<A>*)p; + if ( strcmp(name, atom->name()) == 0 ) + return atom; + p += sizeof(Atom<A>); + } + return NULL; +} + +template <typename A> +void Parser<A>::findTargetFromAddress(pint_t addr, TargetDesc& target) +{ + if ( hasStubsSection() && (_stubsMachOSection->addr() <= addr) && (addr < (_stubsMachOSection->addr()+_stubsMachOSection->size())) ) { + // target is a stub, remove indirection + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); + const macho_nlist<P>& sym = this->symbolFromIndex(symbolIndex); + target.atom = NULL; + target.name = this->nameFromSymbol(sym); + target.weakImport = this->weakImportFromSymbol(sym); + target.addend = 0; + return; + } + Section<A>* section = this->sectionForAddress(addr); + target.atom = section->findAtomByAddress(addr); + target.addend = addr - target.atom->_objAddress; + target.weakImport = false; + target.name = NULL; +} + +template <typename A> +void Parser<A>::findTargetFromAddress(pint_t baseAddr, pint_t addr, TargetDesc& target) +{ + findTargetFromAddress(baseAddr, target); + target.addend = addr - target.atom->_objAddress; +} + +template <typename A> +void Parser<A>::findTargetFromAddressAndSectionNum(pint_t addr, unsigned int sectNum, TargetDesc& target) +{ + if ( sectNum == R_ABS ) { + // target is absolute symbol that corresponds to addr + if ( _absoluteSection != NULL ) { + target.atom = _absoluteSection->findAbsAtomForValue(addr); + if ( target.atom != NULL ) { + target.name = NULL; + target.weakImport = false; + target.addend = 0; + return; + } + } + throwf("R_ABS reloc but no absolute symbol at target address"); + } + + if ( hasStubsSection() && (stubsSectionNum() == sectNum) ) { + // target is a stub, remove indirection + uint32_t symbolIndex = this->symbolIndexFromIndirectSectionAddress(addr, _stubsMachOSection); + assert(symbolIndex != INDIRECT_SYMBOL_LOCAL); + const macho_nlist<P>& sym = this->symbolFromIndex(symbolIndex); + // use direct reference when stub is to a static function + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (this->nameFromSymbol(sym)[0] == 'L')) ) { + this->findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + } + else { + target.atom = NULL; + target.name = this->nameFromSymbol(sym); + target.weakImport = this->weakImportFromSymbol(sym); + target.addend = 0; + } + return; + } + Section<A>* section = this->sectionForNum(sectNum); + target.atom = section->findAtomByAddress(addr); + if ( target.atom == NULL ) { + typedef typename A::P::sint_t sint_t; + sint_t a = (sint_t)addr; + sint_t sectStart = (sint_t)(section->machoSection()->addr()); + sint_t sectEnd = sectStart + section->machoSection()->size(); + if ( a < sectStart ) { + // target address is before start of section, so must be negative addend + target.atom = section->findAtomByAddress(sectStart); + target.addend = a - sectStart; + target.weakImport = false; + target.name = NULL; + return; + } + else if ( a >= sectEnd ) { + target.atom = section->findAtomByAddress(sectEnd-1); + target.addend = a - sectEnd; + target.weakImport = false; + target.name = NULL; + return; + } + } + assert(target.atom != NULL); + target.addend = addr - target.atom->_objAddress; + target.weakImport = false; + target.name = NULL; +} + +template <typename A> +void Parser<A>::addDtraceExtraInfos(const SourceLocation& src, const char* providerName) +{ + // for every ___dtrace_stability$* and ___dtrace_typedefs$* undefine with + // a matching provider name, add a by-name kDtraceTypeReference at probe site + const char* dollar = strchr(providerName, '$'); + if ( dollar != NULL ) { + int providerNameLen = dollar-providerName+1; + for ( std::vector<const char*>::iterator it = _dtraceProviderInfo.begin(); it != _dtraceProviderInfo.end(); ++it) { + const char* typeDollar = strchr(*it, '$'); + if ( typeDollar != NULL ) { + if ( strncmp(typeDollar+1, providerName, providerNameLen) == 0 ) { + addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindDtraceExtra,false, *it); + } + } + } + } +} + +template <typename A> +const char* Parser<A>::scanSymbolTableForAddress(uint64_t addr) +{ + uint64_t closestSymAddr = 0; + const char* closestSymName = NULL; + for (uint32_t i=0; i < this->_symbolCount; ++i) { + const macho_nlist<P>& sym = symbolFromIndex(i); + // ignore stabs + if ( (sym.n_type() & N_STAB) != 0 ) + continue; + + // only look at definitions + if ( (sym.n_type() & N_TYPE) != N_SECT ) + continue; + + // return with exact match + if ( sym.n_value() == addr ) + return nameFromSymbol(sym); + + // record closest seen so far + if ( (sym.n_value() < addr) && ((sym.n_value() > closestSymAddr) || (closestSymName == NULL)) ) + closestSymName = nameFromSymbol(sym); + } + + return (closestSymName != NULL) ? closestSymName : "unknown"; +} + + +template <typename A> +void Parser<A>::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, const TargetDesc& target) +{ + // some fixup pairs can be combined + ld::Fixup::Cluster cl = ld::Fixup::k1of3; + ld::Fixup::Kind firstKind = ld::Fixup::kindSetTargetAddress; + bool combined = false; + if ( target.addend == 0 ) { + cl = ld::Fixup::k1of1; + combined = true; + switch ( setKind ) { + case ld::Fixup::kindStoreLittleEndian32: + firstKind = ld::Fixup::kindStoreTargetAddressLittleEndian32; + break; + case ld::Fixup::kindStoreLittleEndian64: + firstKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; + break; + case ld::Fixup::kindStoreBigEndian32: + firstKind = ld::Fixup::kindStoreTargetAddressBigEndian32; + break; + case ld::Fixup::kindStoreBigEndian64: + firstKind = ld::Fixup::kindStoreTargetAddressBigEndian64; + break; + case ld::Fixup::kindStoreX86BranchPCRel32: + firstKind = ld::Fixup::kindStoreTargetAddressX86BranchPCRel32; + break; + case ld::Fixup::kindStoreX86PCRel32: + firstKind = ld::Fixup::kindStoreTargetAddressX86PCRel32; + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + firstKind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad; + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + firstKind = ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad; + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + firstKind = ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad; + break; + case ld::Fixup::kindStoreARMBranch24: + firstKind = ld::Fixup::kindStoreTargetAddressARMBranch24; + break; + case ld::Fixup::kindStoreThumbBranch22: + firstKind = ld::Fixup::kindStoreTargetAddressThumbBranch22; + break; + case ld::Fixup::kindStorePPCBranch24: + firstKind = ld::Fixup::kindStoreTargetAddressPPCBranch24; + break; + default: + combined = false; + cl = ld::Fixup::k1of2; + break; + } + } + + if ( target.atom != NULL ) { + if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + addFixup(src, cl, firstKind, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + addFixup(src, cl, firstKind, ld::Fixup::bindingByContentBound, target.atom); + } + else if ( (src.atom->section().type() == ld::Section::typeCFString) && (src.offsetInAtom != 0) ) { + // backing string in CFStrings should always be direct + addFixup(src, cl, firstKind, target.atom); + } + else { + // change direct fixup to by-name fixup + addFixup(src, cl, firstKind, false, target.atom->name()); + } + } + else { + addFixup(src, cl, firstKind, target.weakImport, target.name); + } + if ( target.addend == 0 ) { + if ( ! combined ) + addFixup(src, ld::Fixup::k2of2, setKind); + } + else { + addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, target.addend); + addFixup(src, ld::Fixup::k3of3, setKind); + } +} + +template <typename A> +void Parser<A>::addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase) +{ + ld::Fixup::Cluster cl = (target.addend == 0) ? ld::Fixup::k1of4 : ld::Fixup::k1of5; + if ( target.atom != NULL ) { + if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, target.atom); + } + else { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, false, target.atom->name()); + } + } + else { + addFixup(src, cl, ld::Fixup::kindSetTargetAddress, target.weakImport, target.name); + } + if ( target.addend == 0 ) { + assert(picBase.atom != NULL); + addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, picBase.atom); + addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, picBase.addend); + addFixup(src, ld::Fixup::k4of4, kind); + } + else { + addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, target.addend); + addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, picBase.atom); + addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, picBase.addend); + addFixup(src, ld::Fixup::k5of5, kind); + } +} + + + +template <typename A> +uint32_t TentativeDefinitionSection<A>::computeAtomCount(class Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + return parser.tentativeDefinitionCount(); +} + +template <typename A> +uint32_t TentativeDefinitionSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + this->_beginAtoms = (Atom<A>*)p; + uint32_t count = 0; + for (uint32_t i=parser.undefinedStartIndex(); i < parser.undefinedEndIndex(); ++i) { + const macho_nlist<P>& sym = parser.symbolFromIndex(i); + if ( ((sym.n_type() & N_TYPE) == N_UNDF) && (sym.n_value() != 0) ) { + uint64_t size = sym.n_value(); + uint8_t alignP2 = GET_COMM_ALIGN(sym.n_desc()); + if ( alignP2 == 0 ) { + // common symbols align to their size + // that is, a 4-byte common aligns to 4-bytes + // if this size is not a power of two, + // then round up to the next power of two + alignP2 = 63 - (uint8_t)__builtin_clzll(size); + if ( size != (1ULL << alignP2) ) + ++alignP2; + } + // limit alignment of extremely large commons to 2^15 bytes (8-page) + if ( alignP2 > 12 ) + alignP2 = 12; + Atom<A>* allocatedSpace = (Atom<A>*)p; + new (allocatedSpace) Atom<A>(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size, + ld::Atom::definitionTentative, ld::Atom::combineByName, + parser.scopeFromSymbol(sym), ld::Atom::typeZeroFill, ld::Atom::symbolTableIn, + parser.dontDeadStripFromSymbol(sym), false, false, ld::Atom::Alignment(alignP2) ); + p += sizeof(Atom<A>); + ++count; + } + } + this->_endAtoms = (Atom<A>*)p; + return count; +} + + +template <typename A> +uint32_t AbsoluteSymbolSection<A>::computeAtomCount(class Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + return parser.absoluteSymbolCount(); +} + +template <typename A> +uint32_t AbsoluteSymbolSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + this->_beginAtoms = (Atom<A>*)p; + uint32_t count = 0; + for (uint32_t i=0; i < parser.symbolCount(); ++i) { + const macho_nlist<P>& sym = parser.symbolFromIndex(i); + if ( (sym.n_type() & N_TYPE) != N_ABS ) + continue; + const char* absName = parser.nameFromSymbol(sym); + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_class_name_", 17) == 0 ) + continue; + // ignore .objc_class_name_* symbols + if ( strncmp(absName, ".objc_category_name_", 20) == 0 ) + continue; + // ignore empty *.eh symbols + if ( strcmp(&absName[strlen(absName)-3], ".eh") == 0 ) + continue; + + Atom<A>* allocatedSpace = (Atom<A>*)p; + new (allocatedSpace) Atom<A>(*this, parser, sym, 0); + p += sizeof(Atom<A>); + ++count; + } + this->_endAtoms = (Atom<A>*)p; + return count; +} + +template <typename A> +Atom<A>* AbsoluteSymbolSection<A>::findAbsAtomForValue(typename A::P::uint_t value) +{ + Atom<A>* end = this->_endAtoms; + for(Atom<A>* p = this->_beginAtoms; p < end; ++p) { + if ( p->_objAddress == value ) + return p; + } + return NULL; +} + + +template <typename A> +uint32_t Parser<A>::indirectSymbol(uint32_t indirectIndex) +{ + if ( indirectIndex >= _indirectTableCount ) + throw "indirect symbol index out of range"; + return E::get32(_indirectTable[indirectIndex]); +} + +template <typename A> +const macho_nlist<typename A::P>& Parser<A>::symbolFromIndex(uint32_t index) +{ + if ( index > _symbolCount ) + throw "symbol index out of range"; + return _symbols[index]; +} + +template <typename A> +const macho_section<typename A::P>* Parser<A>::machOSectionFromSectionIndex(uint32_t index) +{ + if ( index >= _machOSectionsCount ) + throw "section index out of range"; + return &_sectionsStart[index]; +} + +template <typename A> +uint32_t Parser<A>::symbolIndexFromIndirectSectionAddress(pint_t addr, const macho_section<P>* sect) +{ + uint32_t elementSize = 0; + switch ( sect->flags() & SECTION_TYPE ) { + case S_SYMBOL_STUBS: + elementSize = sect->reserved2(); + break; + case S_LAZY_SYMBOL_POINTERS: + case S_NON_LAZY_SYMBOL_POINTERS: + elementSize = sizeof(pint_t); + break; + default: + throw "section does not use inirect symbol table"; + } + uint32_t indexInSection = (addr - sect->addr()) / elementSize; + uint32_t indexIntoIndirectTable = sect->reserved1() + indexInSection; + return this->indirectSymbol(indexIntoIndirectTable); +} + + + +template <typename A> +const char* Parser<A>::nameFromSymbol(const macho_nlist<P>& sym) +{ + return &_strings[sym.n_strx()]; +} + +template <typename A> +ld::Atom::Scope Parser<A>::scopeFromSymbol(const macho_nlist<P>& sym) +{ + if ( (sym.n_type() & N_EXT) == 0 ) + return ld::Atom::scopeTranslationUnit; + else if ( (sym.n_type() & N_PEXT) != 0 ) + return ld::Atom::scopeLinkageUnit; + else if ( this->nameFromSymbol(sym)[0] == 'l' ) // since all 'l' symbols will be remove, don't make them global + return ld::Atom::scopeLinkageUnit; + else + return ld::Atom::scopeGlobal; +} + +template <typename A> +ld::Atom::Definition Parser<A>::definitionFromSymbol(const macho_nlist<P>& sym) +{ + switch ( sym.n_type() & N_TYPE ) { + case N_ABS: + return ld::Atom::definitionAbsolute; + case N_SECT: + return ld::Atom::definitionRegular; + case N_UNDF: + if ( sym.n_value() != 0 ) + return ld::Atom::definitionTentative; + } + throw "definitionFromSymbol() bad symbol"; +} + +template <typename A> +ld::Atom::Combine Parser<A>::combineFromSymbol(const macho_nlist<P>& sym) +{ + if ( sym.n_desc() & N_WEAK_DEF ) + return ld::Atom::combineByName; + else + return ld::Atom::combineNever; +} + + +template <typename A> +ld::Atom::SymbolTableInclusion Parser<A>::inclusionFromSymbol(const macho_nlist<P>& sym) +{ + const char* symbolName = nameFromSymbol(sym); + // labels beginning with 'l' (lowercase ell) are automatically removed in final linked images <rdar://problem/4571042> + // labels beginning with 'L' should have been stripped by the assembler, so are stripped now + if ( sym.n_desc() & REFERENCED_DYNAMICALLY ) + return ld::Atom::symbolTableInAndNeverStrip; + else if ( symbolName[0] == 'l' ) + return ld::Atom::symbolTableNotInFinalLinkedImages; + else if ( symbolName[0] == 'L' ) + return ld::Atom::symbolTableNotIn; + else + return ld::Atom::symbolTableIn; +} + +template <typename A> +bool Parser<A>::dontDeadStripFromSymbol(const macho_nlist<P>& sym) +{ + return ( (sym.n_desc() & (N_NO_DEAD_STRIP|REFERENCED_DYNAMICALLY)) != 0 ); +} + +template <typename A> +bool Parser<A>::isThumbFromSymbol(const macho_nlist<P>& sym) +{ + return ( sym.n_desc() & N_ARM_THUMB_DEF ); +} + +template <typename A> +bool Parser<A>::weakImportFromSymbol(const macho_nlist<P>& sym) +{ + return ( ((sym.n_type() & N_TYPE) == N_UNDF) && ((sym.n_desc() & N_WEAK_REF) != 0) ); +} + +template <typename A> +bool Parser<A>::resolverFromSymbol(const macho_nlist<P>& sym) +{ + return ( sym.n_desc() & N_SYMBOL_RESOLVER ); +} + + +/* Skip over a LEB128 value (signed or unsigned). */ +static void +skip_leb128 (const uint8_t ** offset, const uint8_t * end) +{ + while (*offset != end && **offset >= 0x80) + (*offset)++; + if (*offset != end) + (*offset)++; +} + +/* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow + or error. On overflow, skip past the rest of the uleb128. */ +static uint64_t +read_uleb128 (const uint8_t ** offset, const uint8_t * end) +{ + uint64_t result = 0; + int bit = 0; + + do { + uint64_t b; + + if (*offset == end) + return (uint64_t) -1; + + b = **offset & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) + result = (uint64_t) -1; + else + result |= b << bit, bit += 7; + } while (*(*offset)++ >= 0x80); + return result; +} + + +/* Skip over a DWARF attribute of form FORM. */ +template <typename A> +bool Parser<A>::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t form, + uint8_t addr_size, bool dwarf64) +{ + int64_t sz=0; + + switch (form) + { + case DW_FORM_addr: + sz = addr_size; + break; + + case DW_FORM_block2: + if (end - *offset < 2) + return false; + sz = 2 + A::P::E::get16(*(uint16_t*)offset); + break; + + case DW_FORM_block4: + if (end - *offset < 4) + return false; + sz = 2 + A::P::E::get32(*(uint32_t*)offset); + break; + + case DW_FORM_data2: + case DW_FORM_ref2: + sz = 2; + break; + + case DW_FORM_data4: + case DW_FORM_ref4: + sz = 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + sz = 8; + break; + + case DW_FORM_string: + while (*offset != end && **offset) + ++*offset; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + sz = 1; + break; + + case DW_FORM_block: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_block1: + if (*offset == end) + return false; + sz = 1 + **offset; + break; + + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + skip_leb128 (offset, end); + return true; + + case DW_FORM_strp: + case DW_FORM_ref_addr: + sz = 4; + break; + + default: + return false; + } + if (end - *offset < sz) + return false; + *offset += sz; + return true; +} + + +template <typename A> +const char* Parser<A>::getDwarfString(uint64_t form, const uint8_t* p) +{ + if ( form == DW_FORM_string ) + return (const char*)p; + else if ( form == DW_FORM_strp ) { + uint32_t offset = E::get32(*((uint32_t*)p)); + const char* dwarfStrings = (char*)_file->fileContent() + _file->_dwarfDebugStringSect->offset(); + if ( offset > _file->_dwarfDebugStringSect->size() ) { + warning("unknown dwarf DW_FORM_strp (offset=0x%08X) is too big in %s\n", offset, this->_path); + return NULL; + } + return &dwarfStrings[offset]; + } + warning("unknown dwarf string encoding (form=%lld) in %s\n", form, this->_path); + return NULL; +} + + +template <typename A> +struct AtomAndLineInfo { + Atom<A>* atom; + ld::Atom::LineInfo info; +}; + + +// <rdar://problem/5591394> Add support to ld64 for N_FUN stabs when used for symbolic constants +// Returns whether a stabStr belonging to an N_FUN stab represents a +// symbolic constant rather than a function +template <typename A> +bool Parser<A>::isConstFunStabs(const char *stabStr) +{ + const char* colon; + // N_FUN can be used for both constants and for functions. In case it's a constant, + // the format of the stabs string is "symname:c=<value>;" + // ':' cannot appear in the symbol name, except if it's an Objective-C method + // (in which case the symbol name starts with + or -, and then it's definitely + // not a constant) + return (stabStr != NULL) && (stabStr[0] != '+') && (stabStr[0] != '-') + && ((colon = strchr(stabStr, ':')) != NULL) + && (colon[1] == 'c') && (colon[2] == '='); +} + + +template <typename A> +void Parser<A>::parseDebugInfo() +{ + // check for dwarf __debug_info section + if ( _file->_dwarfDebugInfoSect == NULL ) { + // if no DWARF debug info, look for stabs + this->parseStabs(); + return; + } + if ( _file->_dwarfDebugInfoSect->size() == 0 ) + return; + + uint64_t stmtList; + if ( !read_comp_unit(&_file->_dwarfTranslationUnitFile, &_file->_dwarfTranslationUnitDir, &stmtList) ) { + // if can't parse dwarf, warn and give up + _file->_dwarfTranslationUnitFile = NULL; + _file->_dwarfTranslationUnitDir = NULL; + warning("can't parse dwarf compilation unit info in %s", _path); + _file->_debugInfoKind = ld::relocatable::File::kDebugInfoNone; + return; + } + + // add line number info to atoms from dwarf + std::vector<AtomAndLineInfo<A> > entries; + entries.reserve(64); + if ( _file->_debugInfoKind == ld::relocatable::File::kDebugInfoDwarf ) { + // file with just data will have no __debug_line info + if ( (_file->_dwarfDebugLineSect != NULL) && (_file->_dwarfDebugLineSect->size() != 0) ) { + // validate stmt_list + if ( (stmtList != (uint64_t)-1) && (stmtList < _file->_dwarfDebugLineSect->size()) ) { + const uint8_t* debug_line = (uint8_t*)_file->fileContent() + _file->_dwarfDebugLineSect->offset(); + struct line_reader_data* lines = line_open(&debug_line[stmtList], + _file->_dwarfDebugLineSect->size() - stmtList, E::little_endian); + struct line_info result; + Atom<A>* curAtom = NULL; + uint32_t curAtomOffset = 0; + uint32_t curAtomAddress = 0; + uint32_t curAtomSize = 0; + std::map<uint32_t,const char*> dwarfIndexToFile; + if ( lines != NULL ) { + while ( line_next(lines, &result, line_stop_pc) ) { + //fprintf(stderr, "curAtom=%p, result.pc=0x%llX, result.line=%llu, result.end_of_sequence=%d," + // " curAtomAddress=0x%X, curAtomSize=0x%X\n", + // curAtom, result.pc, result.line, result.end_of_sequence, curAtomAddress, curAtomSize); + // work around weird debug line table compiler generates if no functions in __text section + if ( (curAtom == NULL) && (result.pc == 0) && result.end_of_sequence && (result.file == 1)) + continue; + // for performance, see if in next pc is in current atom + if ( (curAtom != NULL) && (curAtomAddress <= result.pc) && (result.pc < (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or pc at end of current atom + else if ( result.end_of_sequence && (curAtom != NULL) && (result.pc == (curAtomAddress+curAtomSize)) ) { + curAtomOffset = result.pc - curAtomAddress; + } + // or only one function that is a one line function + else if ( result.end_of_sequence && (curAtom == NULL) && (this->findAtomByAddress(0) != NULL) && (result.pc == this->findAtomByAddress(0)->size()) ) { + curAtom = this->findAtomByAddress(0); + curAtomOffset = result.pc - curAtom->objectAddress(); + curAtomAddress = curAtom->objectAddress(); + curAtomSize = curAtom->size(); + } + else { + // do slow look up of atom by address + try { + curAtom = this->findAtomByAddress(result.pc); + } + catch (...) { + // in case of bug in debug info, don't abort link, just limp on + curAtom = NULL; + } + if ( curAtom == NULL ) + break; // file has line info but no functions + if ( result.end_of_sequence && (curAtomAddress+curAtomSize < result.pc) ) { + // a one line function can be returned by line_next() as one entry with pc at end of blob + // look for alt atom starting at end of previous atom + uint32_t previousEnd = curAtomAddress+curAtomSize; + Atom<A>* alt = this->findAtomByAddressOrNullIfStub(previousEnd); + if ( alt == NULL ) + continue; // ignore spurious debug info for stubs + if ( result.pc <= alt->objectAddress() + alt->size() ) { + curAtom = alt; + curAtomOffset = result.pc - alt->objectAddress(); + curAtomAddress = alt->objectAddress(); + curAtomSize = alt->size(); + } + else { + curAtomOffset = result.pc - curAtom->objectAddress(); + curAtomAddress = curAtom->objectAddress(); + curAtomSize = curAtom->size(); + } + } + else { + curAtomOffset = result.pc - curAtom->objectAddress(); + curAtomAddress = curAtom->objectAddress(); + curAtomSize = curAtom->size(); + } + } + const char* filename; + std::map<uint32_t,const char*>::iterator pos = dwarfIndexToFile.find(result.file); + if ( pos == dwarfIndexToFile.end() ) { + filename = line_file(lines, result.file); + dwarfIndexToFile[result.file] = filename; + } + else { + filename = pos->second; + } + // only record for ~8000 line info records per function + if ( curAtom->roomForMoreLineInfoCount() ) { + AtomAndLineInfo<A> entry; + entry.atom = curAtom; + entry.info.atomOffset = curAtomOffset; + entry.info.fileName = filename; + entry.info.lineNumber = result.line; + //fprintf(stderr, "addr=0x%08llX, line=%lld, file=%s, atom=%s, atom.size=0x%X, end=%d\n", + // result.pc, result.line, filename, curAtom->name(), curAtomSize, result.end_of_sequence); + entries.push_back(entry); + curAtom->incrementLineInfoCount(); + } + if ( result.end_of_sequence ) { + curAtom = NULL; + } + } + line_free(lines); + } + } + } + } + + // assign line info start offset for each atom + uint8_t* p = _file->_atomsArray; + uint32_t liOffset = 0; + for(int i=_file->_atomsArrayCount; i > 0; --i) { + Atom<A>* atom = (Atom<A>*)p; + atom->_lineInfoStartIndex = liOffset; + liOffset += atom->_lineInfoCount; + atom->_lineInfoCount = 0; + p += sizeof(Atom<A>); + } + assert(liOffset == entries.size()); + _file->_lineInfos.reserve(liOffset); + + // copy each line info for each atom + for (typename std::vector<AtomAndLineInfo<A> >::iterator it = entries.begin(); it != entries.end(); ++it) { + uint32_t slot = it->atom->_lineInfoStartIndex + it->atom->_lineInfoCount; + _file->_lineInfos[slot] = it->info; + it->atom->_lineInfoCount++; + } + + // done with temp vector + entries.clear(); +} + +template <typename A> +void Parser<A>::parseStabs() +{ + // scan symbol table for stabs entries + Atom<A>* currentAtom = NULL; + pint_t currentAtomAddress = 0; + enum { start, inBeginEnd, inFun } state = start; + for (uint32_t symbolIndex = 0; symbolIndex < _symbolCount; ++symbolIndex ) { + const macho_nlist<P>& sym = this->symbolFromIndex(symbolIndex); + bool useStab = true; + uint8_t type = sym.n_type(); + const char* symString = (sym.n_strx() != 0) ? this->nameFromSymbol(sym) : NULL; + if ( (type & N_STAB) != 0 ) { + _file->_debugInfoKind = (_hasUUID ? ld::relocatable::File::kDebugInfoStabsUUID : ld::relocatable::File::kDebugInfoStabs); + ld::relocatable::File::Stab stab; + stab.atom = NULL; + stab.type = type; + stab.other = sym.n_sect(); + stab.desc = sym.n_desc(); + stab.value = sym.n_value(); + stab.string = NULL; + switch (state) { + case start: + switch (type) { + case N_BNSYM: + // beginning of function block + state = inBeginEnd; + // fall into case to lookup atom by addresss + case N_LCSYM: + case N_STSYM: + currentAtomAddress = sym.n_value(); + currentAtom = this->findAtomByAddress(currentAtomAddress); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + fprintf(stderr, "can't find atom for stabs BNSYM at %08llX in %s", + (uint64_t)sym.n_value(), _path); + } + break; + case N_SO: + case N_OSO: + case N_OPT: + case N_LSYM: + case N_RSYM: + case N_PSYM: + // not associated with an atom, just copy + stab.string = symString; + break; + case N_GSYM: + { + // n_value field is NOT atom address ;-( + // need to find atom by name match + const char* colon = strchr(symString, ':'); + if ( colon != NULL ) { + // build underscore leading name + int nameLen = colon - symString; + char symName[nameLen+2]; + strlcpy(&symName[1], symString, nameLen+1); + symName[0] = '_'; + symName[nameLen+1] = '\0'; + currentAtom = this->findAtomByName(symName); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + else { + // might be a debug-note without trailing :G() + currentAtom = this->findAtomByName(symString); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + } + if ( stab.atom == NULL ) { + // ld_classic added bogus GSYM stabs for old style dtrace probes + if ( (strncmp(symString, "__dtrace_probe$", 15) != 0) ) + warning("can't find atom for N_GSYM stabs %s in %s", symString, _path); + useStab = false; + } + break; + } + case N_FUN: + if ( isConstFunStabs(symString) ) { + // constant not associated with a function + stab.string = symString; + } + else { + // old style stabs without BNSYM + state = inFun; + currentAtomAddress = sym.n_value(); + currentAtom = this->findAtomByAddress(currentAtomAddress); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, _path); + } + } + break; + case N_SOL: + case N_SLINE: + stab.string = symString; + // old stabs + break; + case N_BINCL: + case N_EINCL: + case N_EXCL: + stab.string = symString; + // -gfull built .o file + break; + default: + warning("unknown stabs type 0x%X in %s", type, _path); + } + break; + case inBeginEnd: + stab.atom = currentAtom; + switch (type) { + case N_ENSYM: + state = start; + currentAtom = NULL; + break; + case N_LCSYM: + case N_STSYM: + { + Atom<A>* nestedAtom = this->findAtomByAddress(sym.n_value()); + if ( nestedAtom != NULL ) { + stab.atom = nestedAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs 0x%X at %08llX in %s", + type, (uint64_t)sym.n_value(), _path); + } + break; + } + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + default: + stab.string = symString; + break; + } + break; + case inFun: + switch (type) { + case N_FUN: + if ( isConstFunStabs(symString) ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + if ( sym.n_sect() != 0 ) { + // found another start stab, must be really old stabs... + currentAtomAddress = sym.n_value(); + currentAtom = this->findAtomByAddress(currentAtomAddress); + if ( currentAtom != NULL ) { + stab.atom = currentAtom; + stab.string = symString; + } + else { + warning("can't find atom for stabs FUN at %08llX in %s", + (uint64_t)currentAtomAddress, _path); + } + } + else { + // found ending stab, switch back to start state + stab.string = symString; + stab.atom = currentAtom; + state = start; + currentAtom = NULL; + } + } + break; + case N_LBRAC: + case N_RBRAC: + case N_SLINE: + // adjust value to be offset in atom + stab.value -= currentAtomAddress; + stab.atom = currentAtom; + break; + case N_SO: + stab.string = symString; + state = start; + break; + default: + stab.atom = currentAtom; + stab.string = symString; + break; + } + break; + } + // add to list of stabs for this .o file + if ( useStab ) + _file->_stabs.push_back(stab); + } + } +} + + + +// Look at the compilation unit DIE and determine +// its NAME, compilation directory (in COMP_DIR) and its +// line number information offset (in STMT_LIST). NAME and COMP_DIR +// may be NULL (especially COMP_DIR) if they are not in the .o file; +// STMT_LIST will be (uint64_t) -1. +// +// At present this assumes that there's only one compilation unit DIE. +// +template <typename A> +bool Parser<A>::read_comp_unit(const char ** name, const char ** comp_dir, + uint64_t *stmt_list) +{ + const uint8_t * debug_info; + const uint8_t * debug_abbrev; + const uint8_t * di; + const uint8_t * da; + const uint8_t * end; + const uint8_t * enda; + uint64_t sz; + uint16_t vers; + uint64_t abbrev_base; + uint64_t abbrev; + uint8_t address_size; + bool dwarf64; + + *name = NULL; + *comp_dir = NULL; + *stmt_list = (uint64_t) -1; + + if ( (_file->_dwarfDebugInfoSect == NULL) || (_file->_dwarfDebugAbbrevSect == NULL) ) + return false; + + debug_info = (uint8_t*)_file->fileContent() + _file->_dwarfDebugInfoSect->offset(); + debug_abbrev = (uint8_t*)_file->fileContent() + _file->_dwarfDebugAbbrevSect->offset(); + di = debug_info; + + if (_file->_dwarfDebugInfoSect->size() < 12) + /* Too small to be a real debug_info section. */ + return false; + sz = A::P::E::get32(*(uint32_t*)di); + di += 4; + dwarf64 = sz == 0xffffffff; + if (dwarf64) + sz = A::P::E::get64(*(uint64_t*)di), di += 8; + else if (sz > 0xffffff00) + /* Unknown dwarf format. */ + return false; + + /* Verify claimed size. */ + if (sz + (di - debug_info) > _file->_dwarfDebugInfoSect->size() || sz <= (dwarf64 ? 23 : 11)) + return false; + + vers = A::P::E::get16(*(uint16_t*)di); + if (vers < 2 || vers > 3) + /* DWARF version wrong for this code. + Chances are we could continue anyway, but we don't know for sure. */ + return false; + di += 2; + + /* Find the debug_abbrev section. */ + abbrev_base = dwarf64 ? A::P::E::get64(*(uint64_t*)di) : A::P::E::get32(*(uint32_t*)di); + di += dwarf64 ? 8 : 4; + + if (abbrev_base > _file->_dwarfDebugAbbrevSect->size()) + return false; + da = debug_abbrev + abbrev_base; + enda = debug_abbrev + _file->_dwarfDebugAbbrevSect->size(); + + address_size = *di++; + + /* Find the abbrev number we're looking for. */ + end = di + sz; + abbrev = read_uleb128 (&di, end); + if (abbrev == (uint64_t) -1) + return false; + + /* Skip through the debug_abbrev section looking for that abbrev. */ + for (;;) + { + uint64_t this_abbrev = read_uleb128 (&da, enda); + uint64_t attr; + + if (this_abbrev == abbrev) + /* This is almost always taken. */ + break; + skip_leb128 (&da, enda); /* Skip the tag. */ + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + do { + attr = read_uleb128 (&da, enda); + skip_leb128 (&da, enda); + } while (attr != 0 && attr != (uint64_t) -1); + if (attr != 0) + return false; + } + + /* Check that the abbrev is one for a DW_TAG_compile_unit. */ + if (read_uleb128 (&da, enda) != DW_TAG_compile_unit) + return false; + if (da == enda) + return false; + da++; /* Skip the DW_CHILDREN_* value. */ + + /* Now, go through the DIE looking for DW_AT_name, + DW_AT_comp_dir, and DW_AT_stmt_list. */ + for (;;) + { + uint64_t attr = read_uleb128 (&da, enda); + uint64_t form = read_uleb128 (&da, enda); + + if (attr == (uint64_t) -1) + return false; + else if (attr == 0) + return true; + + if (form == DW_FORM_indirect) + form = read_uleb128 (&di, end); + + if (attr == DW_AT_name) + *name = getDwarfString(form, di); + else if (attr == DW_AT_comp_dir) + *comp_dir = getDwarfString(form, di); + else if (attr == DW_AT_stmt_list && form == DW_FORM_data4) + *stmt_list = A::P::E::get32(*(uint32_t*)di); + else if (attr == DW_AT_stmt_list && form == DW_FORM_data8) + *stmt_list = A::P::E::get64(*(uint64_t*)di); + if (! skip_form (&di, end, form, address_size, dwarf64)) + return false; + } +} + + + +template <typename A> +File<A>::~File() +{ + free(_sectionsArray); + free(_atomsArray); +} + +template <typename A> +bool File<A>::translationUnitSource(const char** dir, const char** name) const +{ + if ( _debugInfoKind == ld::relocatable::File::kDebugInfoDwarf ) { + *dir = _dwarfTranslationUnitDir; + *name = _dwarfTranslationUnitFile; + return (_dwarfTranslationUnitFile != NULL); + } + return false; +} + + + +template <typename A> +bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + uint8_t* p = _atomsArray; + for(int i=_atomsArrayCount; i > 0; --i) { + handler.doAtom(*((Atom<A>*)p)); + p += sizeof(Atom<A>); + } + return (_atomsArrayCount != 0); +} + +template <typename A> +const char* Section<A>::makeSegmentName(const macho_section<typename A::P>* sect) +{ + // mach-o section record only has room for 16-byte seg/sect names + // so a 16-byte name has no trailing zero + const char* name = sect->segname(); + if ( strlen(name) < 16 ) + return name; + char* tmp = new char[17]; + strlcpy(tmp, name, 17); + return tmp; +} + +template <typename A> +const char* Section<A>::makeSectionName(const macho_section<typename A::P>* sect) +{ + const char* name = sect->sectname(); + if ( strlen(name) < 16 ) + return name; + + // special case common long section names so we don't have to malloc + if ( strncmp(sect->sectname(), "__objc_classrefs", 16) == 0 ) + return "__objc_classrefs"; + if ( strncmp(sect->sectname(), "__objc_classlist", 16) == 0 ) + return "__objc_classlist"; + if ( strncmp(sect->sectname(), "__objc_nlclslist", 16) == 0 ) + return "__objc_nlclslist"; + if ( strncmp(sect->sectname(), "__objc_nlcatlist", 16) == 0 ) + return "__objc_nlcatlist"; + if ( strncmp(sect->sectname(), "__objc_protolist", 16) == 0 ) + return "__objc_protolist"; + if ( strncmp(sect->sectname(), "__objc_protorefs", 16) == 0 ) + return "__objc_protorefs"; + if ( strncmp(sect->sectname(), "__objc_superrefs", 16) == 0 ) + return "__objc_superrefs"; + if ( strncmp(sect->sectname(), "__objc_imageinfo", 16) == 0 ) + return "__objc_imageinfo"; + if ( strncmp(sect->sectname(), "__objc_stringobj", 16) == 0 ) + return "__objc_stringobj"; + if ( strncmp(sect->sectname(), "__gcc_except_tab", 16) == 0 ) + return "__gcc_except_tab"; + + char* tmp = new char[17]; + strlcpy(tmp, name, 17); + return tmp; +} + +template <typename A> +bool Section<A>::readable(const macho_section<typename A::P>* sect) +{ + return true; +} + +template <typename A> +bool Section<A>::writable(const macho_section<typename A::P>* sect) +{ + // mach-o .o files do not contain segment permissions + // we just know TEXT is special + return ( strcmp(sect->segname(), "__TEXT") != 0 ); +} + +template <typename A> +bool Section<A>::exectuable(const macho_section<typename A::P>* sect) +{ + // mach-o .o files do not contain segment permissions + // we just know TEXT is special + return ( strcmp(sect->segname(), "__TEXT") == 0 ); +} + + +template <typename A> +ld::Section::Type Section<A>::sectionType(const macho_section<typename A::P>* sect) +{ + switch ( sect->flags() & SECTION_TYPE ) { + case S_ZEROFILL: + return ld::Section::typeZeroFill; + case S_CSTRING_LITERALS: + if ( (strcmp(sect->sectname(), "__cstring") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) + return ld::Section::typeCString; + else + return ld::Section::typeNonStdCString; + case S_4BYTE_LITERALS: + return ld::Section::typeLiteral4; + case S_8BYTE_LITERALS: + return ld::Section::typeLiteral8; + case S_LITERAL_POINTERS: + return ld::Section::typeCStringPointer; + case S_NON_LAZY_SYMBOL_POINTERS: + return ld::Section::typeNonLazyPointer; + case S_LAZY_SYMBOL_POINTERS: + return ld::Section::typeLazyPointer; + case S_SYMBOL_STUBS: + return ld::Section::typeStub; + case S_MOD_INIT_FUNC_POINTERS: + return ld::Section::typeInitializerPointers; + case S_MOD_TERM_FUNC_POINTERS: + return ld::Section::typeTerminatorPointers; + case S_INTERPOSING: + return ld::Section::typeUnclassified; + case S_16BYTE_LITERALS: + return ld::Section::typeLiteral16; + case S_REGULAR: + case S_COALESCED: + if ( sect->flags() & S_ATTR_PURE_INSTRUCTIONS ) { + return ld::Section::typeCode; + } + else if ( strcmp(sect->segname(), "__TEXT") == 0 ) { + if ( strcmp(sect->sectname(), "__eh_frame") == 0 ) + return ld::Section::typeCFI; + else if ( strcmp(sect->sectname(), "__ustring") == 0 ) + return ld::Section::typeUTF16Strings; + else if ( strcmp(sect->sectname(), "__textcoal_nt") == 0 ) + return ld::Section::typeCode; + else if ( strcmp(sect->sectname(), "__StaticInit") == 0 ) + return ld::Section::typeCode; + } + else if ( strcmp(sect->segname(), "__DATA") == 0 ) { + if ( strcmp(sect->sectname(), "__cfstring") == 0 ) + return ld::Section::typeCFString; + else if ( strcmp(sect->sectname(), "__dyld") == 0 ) + return ld::Section::typeDyldInfo; + else if ( strcmp(sect->sectname(), "__program_vars") == 0 ) + return ld::Section::typeDyldInfo; + else if ( strncmp(sect->sectname(), "__objc_classrefs", 16) == 0 ) + return ld::Section::typeObjCClassRefs; + else if ( strcmp(sect->sectname(), "__objc_catlist") == 0 ) + return ld::Section::typeObjC2CategoryList; + } + else if ( strcmp(sect->segname(), "__OBJC") == 0 ) { + if ( strcmp(sect->sectname(), "__class") == 0 ) + return ld::Section::typeObjC1Classes; + } + break; + case S_THREAD_LOCAL_REGULAR: + return ld::Section::typeTLVInitialValues; + case S_THREAD_LOCAL_ZEROFILL: + return ld::Section::typeTLVZeroFill; + case S_THREAD_LOCAL_VARIABLES: + return ld::Section::typeTLVDefs; + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + return ld::Section::typeTLVInitializerPointers; + } + return ld::Section::typeUnclassified; +} + + +template <typename A> +Atom<A>* Section<A>::findContentAtomByAddress(pint_t addr, class Atom<A>* start, class Atom<A>* end) +{ + // do a binary search of atom array + uint32_t atomCount = end - start; + Atom<A>* base = start; + for (uint32_t n = atomCount; n > 0; n /= 2) { + Atom<A>* pivot = &base[n/2]; + pint_t atomStartAddr = pivot->_objAddress; + pint_t atomEndAddr = atomStartAddr + pivot->_size; + if ( atomStartAddr <= addr ) { + // address in normal atom + if (addr < atomEndAddr) + return pivot; + // address in "end" label (but not in alias) + if ( (pivot->_size == 0) && (addr == atomEndAddr) && !pivot->isAlias() ) + return pivot; + } + if ( addr >= atomEndAddr ) { + // key > pivot + // move base to atom after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + +template <typename A> +ld::Atom::Alignment Section<A>::alignmentForAddress(pint_t addr) +{ + const uint32_t sectionAlignment = this->_machOSection->align(); + return ld::Atom::Alignment(sectionAlignment, (addr % (1 << sectionAlignment))); +} + +template <typename A> +uint32_t Section<A>::sectionNum(class Parser<A>& parser) const +{ + if ( _machOSection == NULL ) + return 0; + else + return 1 + (this->_machOSection - parser.firstMachOSection()); +} + +// libunwind does not support ppc64 +template <> uint32_t CFISection<ppc64>::cfiCount() { return 0; } +// arm does not have zero cost exceptions +template <> uint32_t CFISection<arm>::cfiCount() { return 0; } + +template <typename A> +uint32_t CFISection<A>::cfiCount() +{ + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + return libunwind::CFI_Parser<OAS>::getCFICount(oas, + this->_machOSection->addr(), this->_machOSection->size()); +} + +template <typename A> +void CFISection<A>::warnFunc(void* ref, uint64_t funcAddr, const char* msg) +{ + Parser<A>* parser = (Parser<A>*)ref; + if ( ! parser->convertUnwindInfo() ) + return; + if ( funcAddr != CFI_INVALID_ADDRESS ) { + // atoms are not constructed yet, so scan symbol table for labels + const char* name = parser->scanSymbolTableForAddress(funcAddr); + warning("could not create compact unwind for %s: %s", name, msg); + } + else { + warning("could not create compact unwind: %s", msg); + } +} + +template <> +bool CFISection<x86_64>::needsRelocating() +{ + return true; +} + +template <typename A> +bool CFISection<A>::needsRelocating() +{ + return false; +} + +template <> +void CFISection<x86_64>::cfiParse(class Parser<x86_64>& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info<CFISection<x86_64>::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // copy __eh_frame data to buffer + memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); + + // and apply relocations + const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(file().fileContent() + this->_machOSection->reloff()); + const macho_relocation_info<P>* relocsEnd = &relocs[this->_machOSection->nreloc()]; + for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) { + uint64_t value = 0; + switch ( reloc->r_type() ) { + case X86_64_RELOC_SUBTRACTOR: + value = 0 - parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + ++reloc; + if ( reloc->r_extern() ) + value += parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case X86_64_RELOC_UNSIGNED: + value = parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case X86_64_RELOC_GOT: + // this is used for the reference to the personality function in CIEs + // store the symbol number of the personality function for later use as a Fixup + value = reloc->r_symbolnum(); + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address()); + break; + } + uint64_t* p64; + uint32_t* p32; + switch ( reloc->r_length() ) { + case 3: + p64 = (uint64_t*)&buffer[reloc->r_address()]; + E::set64(*p64, value + E::get64(*p64)); + break; + case 2: + p32 = (uint32_t*)&buffer[reloc->r_address()]; + E::set32(*p32, value + E::get32(*p32)); + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation size at r_address=0x%08X\n", reloc->r_address()); + break; + } + } + + + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, buffer); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions<OAS, libunwind::Registers_x86_64>::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} + +template <> +void CFISection<x86>::cfiParse(class Parser<x86>& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info<CFISection<x86>::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions<OAS, libunwind::Registers_x86>::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} + + +// need to change libunwind parseCFIs() to work for ppc +template <> +void CFISection<ppc>::cfiParse(class Parser<ppc>& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info<CFISection<ppc>::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions<OAS, libunwind::Registers_ppc>::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} + +template <> +void CFISection<ppc64>::cfiParse(class Parser<ppc64>& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info<CFISection<ppc64>::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // libunwind does not support ppc64 + assert(count == 0); +} + +template <> +void CFISection<arm>::cfiParse(class Parser<arm>& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info<CFISection<arm>::OAS>::CFI_Atom_Info cfiArray[], + uint32_t count) +{ + // arm does not use zero cost exceptions + assert(count == 0); +} + + + +template <typename A> +uint32_t CFISection<A>::computeAtomCount(class Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray& cfis) +{ + return cfis.count; +} + + + +template <typename A> +uint32_t CFISection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray& cfis) +{ + this->_beginAtoms = (Atom<A>*)p; + // walk CFI_Atom_Info array and create atom for each entry + const CFI_Atom_Info* start = &cfis.array[0]; + const CFI_Atom_Info* end = &cfis.array[cfis.count]; + for(const CFI_Atom_Info* a=start; a < end; ++a) { + Atom<A>* space = (Atom<A>*)p; + new (space) Atom<A>(*this, (a->isCIE ? "CIE" : "FDE"), a->address, a->size, + ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, + ld::Atom::typeCFI, ld::Atom::symbolTableNotInFinalLinkedImages, + false, false, false, ld::Atom::Alignment(0)); + p += sizeof(Atom<A>); + } + this->_endAtoms = (Atom<A>*)p; + return cfis.count; +} + + +template <> bool CFISection<x86_64>::bigEndian() { return false; } +template <> bool CFISection<x86>::bigEndian() { return false; } +template <> bool CFISection<arm>::bigEndian() { return false; } +template <> bool CFISection<ppc>::bigEndian() { return true; } +template <> bool CFISection<ppc64>::bigEndian() { return true; } + + +template <> +void CFISection<x86_64>::addCiePersonalityFixups(class Parser<x86_64>& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( personalityEncoding == 0x9B ) { + // compiler always produces X86_64_RELOC_GOT with addend of 4 to personality function + // CFISection<x86_64>::cfiParse() set targetAddress to be symbolIndex + 4 + addressInCIE + uint32_t symbolIndex = cieInfo->u.cieInfo.personality.targetAddress - 4 + - cieInfo->address - cieInfo->u.cieInfo.personality.offsetInCFI; + const macho_nlist<P>& sym = parser.symbolFromIndex(symbolIndex); + const char* personalityName = parser.nameFromSymbol(sym); + + Atom<x86_64>* cieAtom = this->findAtomByAddress(cieInfo->address); + Parser<x86_64>::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, false, personalityName); + parser.addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, 4); + parser.addFixup(src, ld::Fixup::k3of3, ld::Fixup::kindStoreX86PCRel32GOT); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} + +template <> +void CFISection<x86>::addCiePersonalityFixups(class Parser<x86>& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { + uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; + uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; + Atom<x86>* cieAtom = this->findAtomByAddress(cieInfo->address); + Atom<x86>* nlpAtom = parser.findAtomByAddress(nlpAddr); + assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); + Parser<x86>::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", personalityEncoding); + } +} + + +template <> +void CFISection<ppc>::addCiePersonalityFixups(class Parser<ppc>& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( (personalityEncoding == 0x9B) || (personalityEncoding == 0x90) ) { + uint32_t offsetInCFI = cieInfo->u.cieInfo.personality.offsetInCFI; + uint32_t nlpAddr = cieInfo->u.cieInfo.personality.targetAddress; + Atom<ppc>* cieAtom = this->findAtomByAddress(cieInfo->address); + Atom<ppc>* nlpAtom = parser.findAtomByAddress(nlpAddr); + assert(nlpAtom->contentType() == ld::Atom::typeNonLazyPointer); + Parser<ppc>::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, nlpAtom); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, offsetInCFI); + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreBigEndian32); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} + + +template <typename A> +void CFISection<A>::addCiePersonalityFixups(class Parser<A>& parser, const CFI_Atom_Info* cieInfo) +{ + // FIX ME + assert(0); +} + +template <typename A> +void CFISection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray& cfis) +{ + ld::Fixup::Kind store32 = bigEndian() ? ld::Fixup::kindStoreBigEndian32 : ld::Fixup::kindStoreLittleEndian32; + ld::Fixup::Kind store64 = bigEndian() ? ld::Fixup::kindStoreBigEndian64 : ld::Fixup::kindStoreLittleEndian64; + + // add all references for FDEs, including implicit group references + const CFI_Atom_Info* end = &cfis.array[cfis.count]; + for(const CFI_Atom_Info* p = &cfis.array[0]; p < end; ++p) { + if ( p->isCIE ) { + // add reference to personality function if used + if ( p->u.cieInfo.personality.targetAddress != CFI_INVALID_ADDRESS ) { + this->addCiePersonalityFixups(parser, p); + } + } + else { + // find FDE Atom + Atom<A>* fdeAtom = this->findAtomByAddress(p->address); + // find function Atom + Atom<A>* functionAtom = parser.findAtomByAddress(p->u.fdeInfo.function.targetAddress); + // find CIE Atom + Atom<A>* cieAtom = this->findAtomByAddress(p->u.fdeInfo.cie.targetAddress); + // find LSDA Atom + Atom<A>* lsdaAtom = NULL; + if ( p->u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) { + lsdaAtom = parser.findAtomByAddress(p->u.fdeInfo.lsda.targetAddress); + } + // add reference from FDE to CIE (always 32-bit pc-rel) + typename Parser<A>::SourceLocation fdeToCieSrc(fdeAtom, p->u.fdeInfo.cie.offsetInCFI); + parser.addFixup(fdeToCieSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, fdeAtom); + parser.addFixup(fdeToCieSrc, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, p->u.fdeInfo.cie.offsetInCFI); + parser.addFixup(fdeToCieSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, cieAtom); + parser.addFixup(fdeToCieSrc, ld::Fixup::k4of4, store32, cieAtom); + + // add reference from FDE to function + typename Parser<A>::SourceLocation fdeToFuncSrc(fdeAtom, p->u.fdeInfo.function.offsetInCFI); + switch (p->u.fdeInfo.function.encodingOfTargetAddress) { + case DW_EH_PE_pcrel|DW_EH_PE_ptr: + if ( sizeof(typename A::P::uint_t) == 8 ) { + parser.addFixup(fdeToFuncSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, functionAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.function.offsetInCFI); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k4of4, store64); + break; + } + // else fall into 32-bit case + case DW_EH_PE_pcrel|DW_EH_PE_sdata4: + parser.addFixup(fdeToFuncSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, functionAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.function.offsetInCFI); + parser.addFixup(fdeToFuncSrc, ld::Fixup::k4of4, store32); + break; + default: + throw "unsupported encoding in FDE of pointer to function"; + } + + // add reference from FDE to LSDA + typename Parser<A>::SourceLocation fdeToLsdaSrc(fdeAtom, p->u.fdeInfo.lsda.offsetInCFI); + if ( lsdaAtom != NULL ) { + switch (p->u.fdeInfo.lsda.encodingOfTargetAddress) { + case DW_EH_PE_pcrel|DW_EH_PE_ptr: + if ( sizeof(typename A::P::uint_t) == 8 ) { + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, lsdaAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.lsda.offsetInCFI); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k4of4, store64); + break; + } + // else fall into 32-bit case + case DW_EH_PE_pcrel|DW_EH_PE_sdata4: + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, lsdaAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, fdeAtom); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, p->u.fdeInfo.lsda.offsetInCFI); + parser.addFixup(fdeToLsdaSrc, ld::Fixup::k4of4, store32); + break; + default: + throw "unsupported encoding in FDE of pointer to LSDA"; + } + } + + // FDE is in group lead by function atom + typename Parser<A>::SourceLocation fdeSrc(functionAtom,0); + parser.addFixup(fdeSrc, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateFDE, fdeAtom); + + // LSDA is in group lead by function atom + if ( lsdaAtom != NULL ) { + parser.addFixup(fdeSrc, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, lsdaAtom); + } + } + } +} + + + + +template <typename A> +const void* CFISection<A>::OAS::mappedAddress(pint_t addr) +{ + if ( (_ehFrameStartAddr <= addr) && (addr < _ehFrameEndAddr) ) + return &_ehFrameContent[addr-_ehFrameStartAddr]; + else { + // requested bytes are not in __eh_frame section + // this can occur when examining the instruction bytes in the __text + File<A>& file = _ehFrameSection.file(); + for (uint32_t i=0; i < file._sectionsArrayCount; ++i ) { + const macho_section<typename A::P>* sect = file._sectionsArray[i]->machoSection(); + // TentativeDefinitionSection and AbsoluteSymbolSection have no mach-o section + if ( sect != NULL ) { + if ( (sect->addr() <= addr) && (addr < (sect->addr()+sect->size())) ) { + return file.fileContent() + sect->offset() + addr - sect->addr(); + } + } + } + throwf("__eh_frame parsing problem. Can't find target of reference to address 0x%08llX", (uint64_t)addr); + } +} + + +template <typename A> +uint64_t CFISection<A>::OAS::getULEB128(pint_t& logicalAddr, pint_t end) +{ + uintptr_t size = (end - logicalAddr); + libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); + libunwind::LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = libunwind::LocalAddressSpace::getULEB128(laddr, laddr+size); + logicalAddr += (laddr-sladdr); + return result; +} + +template <typename A> +int64_t CFISection<A>::OAS::getSLEB128(pint_t& logicalAddr, pint_t end) +{ + uintptr_t size = (end - logicalAddr); + libunwind::LocalAddressSpace::pint_t laddr = (libunwind::LocalAddressSpace::pint_t)mappedAddress(logicalAddr); + libunwind::LocalAddressSpace::pint_t sladdr = laddr; + int64_t result = libunwind::LocalAddressSpace::getSLEB128(laddr, laddr+size); + logicalAddr += (laddr-sladdr); + return result; +} + +template <typename A> +typename A::P::uint_t CFISection<A>::OAS::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + pint_t p = addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + throwf("ObjectFileAddressSpace<A>::getEncodedP() encoding 0x%08X not supported", encoding); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + throw "DW_EH_PE_textrel pointer encoding not supported"; + break; + case DW_EH_PE_datarel: + throw "DW_EH_PE_datarel pointer encoding not supported"; + break; + case DW_EH_PE_funcrel: + throw "DW_EH_PE_funcrel pointer encoding not supported"; + break; + case DW_EH_PE_aligned: + throw "DW_EH_PE_aligned pointer encoding not supported"; + break; + default: + throwf("ObjectFileAddressSpace<A>::getEncodedP() encoding 0x%08X not supported", encoding); + break; + } + +// Note: DW_EH_PE_indirect is only used in CIEs to refernce the personality pointer +// When parsing .o files that pointer contains zero, so we don't to return that. +// Instead we skip the dereference and return the address of the pointer. +// if ( encoding & DW_EH_PE_indirect ) +// result = getP(result); + + return result; +} + +template <typename A> +SymboledSection<A>::SymboledSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s) + : Section<A>(f, s), _type(ld::Atom::typeUnclassified) +{ + switch ( s->flags() & SECTION_TYPE ) { + case S_ZEROFILL: + _type = ld::Atom::typeZeroFill; + break; + case S_MOD_INIT_FUNC_POINTERS: + _type = ld::Atom::typeInitializerPointers; + break; + case S_MOD_TERM_FUNC_POINTERS: + _type = ld::Atom::typeTerminatorPointers; + break; + case S_THREAD_LOCAL_VARIABLES: + _type = ld::Atom::typeTLV; + break; + case S_THREAD_LOCAL_ZEROFILL: + _type = ld::Atom::typeTLVZeroFill; + break; + case S_THREAD_LOCAL_REGULAR: + _type = ld::Atom::typeTLVInitialValue; + break; + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + _type = ld::Atom::typeTLVInitializerPointers; + break; + case S_REGULAR: + if ( strncmp(s->sectname(), "__gcc_except_tab", 16) == 0 ) + _type = ld::Atom::typeLSDA; + break; + } +} + + +template <typename A> +bool SymboledSection<A>::dontDeadStrip() +{ + switch ( _type ) { + case ld::Atom::typeInitializerPointers: + case ld::Atom::typeTerminatorPointers: + return true; + default: + // model an object file without MH_SUBSECTIONS_VIA_SYMBOLS as one in which nothing can be dead stripped + if ( ! this->_file.canScatterAtoms() ) + return true; + // call inherited + return Section<A>::dontDeadStrip(); + } + return false; +} + + +template <typename A> +uint32_t SymboledSection<A>::computeAtomCount(class Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + const pint_t startAddr = this->_machOSection->addr(); + const pint_t endAddr = startAddr + this->_machOSection->size(); + const uint32_t sectNum = this->sectionNum(parser); + + uint32_t count = 0; + pint_t addr; + pint_t size; + const macho_nlist<P>* sym; + while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &sym) ) { + ++count; + } + //fprintf(stderr, "computeAtomCount(%s,%s) => %d\n", this->segmentName(), this->sectionName(), count); + return count; +} + +template <typename A> +uint32_t SymboledSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + this->_beginAtoms = (Atom<A>*)p; + + //fprintf(stderr, "SymboledSection::appendAtoms() in section %s\n", this->_machOSection->sectname()); + const pint_t startAddr = this->_machOSection->addr(); + const pint_t endAddr = startAddr + this->_machOSection->size(); + const uint32_t sectNum = this->sectionNum(parser); + + uint32_t count = 0; + pint_t addr; + pint_t size; + const macho_nlist<P>* label; + while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &label) ) { + Atom<A>* allocatedSpace = (Atom<A>*)p; + // is break because of label or CFI? + if ( label != NULL ) { + // The size is computed based on the address of the next label (or the end of the section for the last label) + // If there are two labels at the same address, we want them one to be an alias of the other. + // If the label is at the end of a section, it is has zero size, but is not an alias + const bool isAlias = ( (size == 0) && (addr < endAddr) ); + new (allocatedSpace) Atom<A>(*this, parser, *label, size, isAlias); + if ( isAlias ) + this->_hasAliases = true; + } + else { + new (allocatedSpace) Atom<A>(*this, "anon", addr, size, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, this->contentType(), ld::Atom::symbolTableNotIn, + this->dontDeadStrip(), false, false, this->alignmentForAddress(addr)); + } + p += sizeof(Atom<A>); + ++count; + } + + this->_endAtoms = (Atom<A>*)p; + return count; +} + + +template <typename A> +uint32_t ImplicitSizeSection<A>::computeAtomCount(class Parser<A>& parser, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + uint32_t count = 0; + const macho_section<P>* sect = this->machoSection(); + const pint_t startAddr = sect->addr(); + const pint_t endAddr = startAddr + sect->size(); + for (pint_t addr = startAddr; addr < endAddr; addr += elementSizeAtAddress(addr) ) { + if ( useElementAt(parser, it, addr) ) + ++count; + } + if ( it.fileHasOverlappingSymbols && (sect->size() != 0) && (this->combine(parser, startAddr) == ld::Atom::combineByNameAndContent) ) { + // if there are multiple labels in this section for the same address, then clone them into multi atoms + pint_t prevSymbolAddr = (pint_t)(-1); + uint8_t prevSymbolSectNum = 0; + for(uint32_t i=0; i < it.sortedSymbolCount; ++i) { + const macho_nlist<P>& sym = parser.symbolFromIndex(it.sortedSymbolIndexes[i]); + const pint_t symbolAddr = sym.n_value(); + const pint_t symbolSectNum = sym.n_sect(); + if ( (symbolAddr == prevSymbolAddr) && (prevSymbolSectNum == symbolSectNum) && (symbolSectNum == this->sectionNum(parser)) ) { + ++count; + } + prevSymbolAddr = symbolAddr; + prevSymbolSectNum = symbolSectNum; + } + } + return count; +} + +template <typename A> +uint32_t ImplicitSizeSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p, + struct Parser<A>::LabelAndCFIBreakIterator& it, + const struct Parser<A>::CFIInfoArray&) +{ + this->_beginAtoms = (Atom<A>*)p; + + const macho_section<P>* sect = this->machoSection(); + const pint_t startAddr = sect->addr(); + const pint_t endAddr = startAddr + sect->size(); + const uint32_t sectNum = this->sectionNum(parser); + //fprintf(stderr, "ImplicitSizeSection::appendAtoms() in section %s\n", sect->sectname()); + uint32_t count = 0; + pint_t foundAddr; + pint_t size; + const macho_nlist<P>* foundLabel; + Atom<A>* allocatedSpace; + while ( it.next(parser, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { + if ( foundLabel != NULL ) { + pint_t labeledAtomSize = this->elementSizeAtAddress(foundAddr); + allocatedSpace = (Atom<A>*)p; + if ( this->ignoreLabel(parser.nameFromSymbol(*foundLabel)) ) { + //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)foundAddr); + new (allocatedSpace) Atom<A>(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, + this->elementSizeAtAddress(foundAddr), this->definition(), + this->combine(parser, foundAddr), this->scopeAtAddress(parser, foundAddr), + this->contentType(), this->symbolTableInclusion(), + this->dontDeadStrip(), false, false, this->alignmentForAddress(foundAddr)); + } + else { + // make named atom for label + //fprintf(stderr, " 0x%08llX make labeled\n", (uint64_t)foundAddr); + new (allocatedSpace) Atom<A>(*this, parser, *foundLabel, labeledAtomSize); + } + ++count; + p += sizeof(Atom<A>); + foundAddr += labeledAtomSize; + size -= labeledAtomSize; + } + // some number of anonymous atoms + for (pint_t addr = foundAddr; addr < (foundAddr+size); addr += elementSizeAtAddress(addr) ) { + // make anon atoms for area before label + if ( this->useElementAt(parser, it, addr) ) { + //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)addr); + allocatedSpace = (Atom<A>*)p; + new (allocatedSpace) Atom<A>(*this, this->unlabeledAtomName(parser, addr), addr, this->elementSizeAtAddress(addr), + this->definition(), this->combine(parser, addr), this->scopeAtAddress(parser, addr), + this->contentType(), this->symbolTableInclusion(), + this->dontDeadStrip(), false, false, this->alignmentForAddress(addr)); + ++count; + p += sizeof(Atom<A>); + } + } + } + + this->_endAtoms = (Atom<A>*)p; + + return count; +} + + +template <typename A> +unsigned long Literal4Section<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + const uint32_t* literalContent = (uint32_t*)atom->contentPointer(); + return *literalContent; +} + +template <typename A> +bool Literal4Section<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + assert(this->type() == rhs.section().type()); + const uint32_t* literalContent = (uint32_t*)atom->contentPointer(); + + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + const uint32_t* rhsLiteralContent = (uint32_t*)rhsAtom->contentPointer(); + return (*literalContent == *rhsLiteralContent); + } + return false; +} + + +template <typename A> +unsigned long Literal8Section<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ +#if __LP64__ + const uint64_t* literalContent = (uint64_t*)atom->contentPointer(); + return *literalContent; +#else + unsigned long hash = 5381; + const uint8_t* byteContent = atom->contentPointer(); + for (int i=0; i < 8; ++i) { + hash = hash * 33 + byteContent[i]; + } + return hash; +#endif +} + +template <typename A> +bool Literal8Section<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeLiteral8 ) + return false; + assert(this->type() == rhs.section().type()); + const uint64_t* literalContent = (uint64_t*)atom->contentPointer(); + + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + const uint64_t* rhsLiteralContent = (uint64_t*)rhsAtom->contentPointer(); + return (*literalContent == *rhsLiteralContent); + } + return false; +} + + +template <typename A> +unsigned long Literal16Section<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 5381; + const uint8_t* byteContent = atom->contentPointer(); + for (int i=0; i < 16; ++i) { + hash = hash * 33 + byteContent[i]; + } + return hash; +} + +template <typename A> +bool Literal16Section<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeLiteral16 ) + return false; + assert(this->type() == rhs.section().type()); + const uint64_t* literalContent = (uint64_t*)atom->contentPointer(); + + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + const uint64_t* rhsLiteralContent = (uint64_t*)rhsAtom->contentPointer(); + return ((literalContent[0] == rhsLiteralContent[0]) && (literalContent[1] == rhsLiteralContent[1])); + } + return false; +} + + + +template <typename A> +typename A::P::uint_t CStringSection<A>::elementSizeAtAddress(pint_t addr) +{ + const macho_section<P>* sect = this->machoSection(); + const char* stringContent = (char*)(this->file().fileContent() + sect->offset() + addr - sect->addr()); + return strlen(stringContent) + 1; +} + +template <typename A> +bool CStringSection<A>::useElementAt(Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, pint_t addr) +{ + return true; +} + +template <typename A> +Atom<A>* CStringSection<A>::findAtomByAddress(pint_t addr) +{ + Atom<A>* result = this->findContentAtomByAddress(addr, this->_beginAtoms, this->_endAtoms); + return result; +} + +template <typename A> +unsigned long CStringSection<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 5381; + const char* stringContent = (char*)atom->contentPointer(); + for (const char* s = stringContent; *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + + +template <typename A> +bool CStringSection<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeCString ) + return false; + assert(this->type() == rhs.section().type()); + assert(strcmp(this->sectionName(), rhs.section().sectionName())== 0); + assert(strcmp(this->segmentName(), rhs.section().segmentName())== 0); + const char* stringContent = (char*)atom->contentPointer(); + + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + if ( rhsAtom != NULL ) { + if ( atom->_size != rhsAtom->_size ) + return false; + const char* rhsStringContent = (char*)rhsAtom->contentPointer(); + return (strcmp(stringContent, rhsStringContent) == 0); + } + return false; +} + + +template <> +ld::Fixup::Kind NonLazyPointerSection<x86>::fixupKind() +{ + return ld::Fixup::kindStoreLittleEndian32; +} + +template <> +ld::Fixup::Kind NonLazyPointerSection<arm>::fixupKind() +{ + return ld::Fixup::kindStoreLittleEndian32; +} + +template <> +ld::Fixup::Kind NonLazyPointerSection<ppc>::fixupKind() +{ + return ld::Fixup::kindStoreBigEndian32; +} + +template <> +ld::Fixup::Kind NonLazyPointerSection<ppc64>::fixupKind() +{ + return ld::Fixup::kindStoreBigEndian64; +} + +template <> +void NonLazyPointerSection<x86_64>::makeFixups(class Parser<x86_64>& parser, const struct Parser<x86_64>::CFIInfoArray&) +{ + assert(0 && "x86_64 should not have non-lazy-pointer sections in .o files"); +} + +template <typename A> +void NonLazyPointerSection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&) +{ + // add references for each NLP atom based on indirect symbol table + const macho_section<P>* sect = this->machoSection(); + const pint_t endAddr = sect->addr() + sect->size(); + for( pint_t addr = sect->addr(); addr < endAddr; addr += sizeof(pint_t)) { + typename Parser<A>::SourceLocation src; + typename Parser<A>::TargetDesc target; + src.atom = this->findAtomByAddress(addr); + src.offsetInAtom = 0; + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect); + target.atom = NULL; + target.name = NULL; + target.weakImport = false; + target.addend = 0; + if ( symIndex == INDIRECT_SYMBOL_LOCAL ) { + // use direct reference for local symbols + const pint_t* nlpContent = (pint_t*)(this->file().fileContent() + sect->offset() + addr - sect->addr()); + pint_t targetAddr = P::getP(*nlpContent); + target.atom = parser.findAtomByAddress(targetAddr); + target.weakImport = false; + target.addend = (targetAddr - target.atom->objectAddress()); + // <rdar://problem/8385011> if pointer to thumb function, mask of thumb bit (not an addend of +1) + if ( target.atom->isThumb() ) + target.addend &= (-2); + assert(src.atom->combine() == ld::Atom::combineNever); + } + else { + const macho_nlist<P>& sym = parser.symbolFromIndex(symIndex); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + assert(src.atom->combine() == ld::Atom::combineNever); + } + else { + target.name = parser.nameFromSymbol(sym); + target.weakImport = parser.weakImportFromSymbol(sym); + assert(src.atom->combine() == ld::Atom::combineByNameAndReferences); + } + } + parser.addFixups(src, this->fixupKind(), target); + } +} + +template <typename A> +ld::Atom::Combine NonLazyPointerSection<A>::combine(Parser<A>& parser, pint_t addr) +{ + const macho_section<P>* sect = this->machoSection(); + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect); + if ( symIndex == INDIRECT_SYMBOL_LOCAL) + return ld::Atom::combineNever; + + // don't coalesce non-lazy-pointers to local symbols + const macho_nlist<P>& sym = parser.symbolFromIndex(symIndex); + if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) + return ld::Atom::combineNever; + + return ld::Atom::combineByNameAndReferences; +} + +template <typename A> +const char* NonLazyPointerSection<A>::targetName(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + assert(atom->fixupCount() == 1); + ld::Fixup::iterator fit = atom->fixupsBegin(); + const char* name = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + name = fit->u.name; + break; + case ld::Fixup::bindingByContentBound: + name = fit->u.target->name(); + break; + case ld::Fixup::bindingsIndirectlyBound: + name = ind.indirectName(fit->u.bindingIndex); + break; + default: + assert(0); + } + assert(name != NULL); + return name; +} + +template <typename A> +unsigned long NonLazyPointerSection<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + unsigned long hash = 9508; + for (const char* s = this->targetName(atom, ind); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template <typename A> +bool NonLazyPointerSection<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + if ( rhs.section().type() != ld::Section::typeNonLazyPointer ) + return false; + assert(this->type() == rhs.section().type()); + // there can be many non-lazy pointer in different section names + // we only want to coalesce in same section name + if ( *this != rhs.section() ) + return false; + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + const char* thisName = this->targetName(atom, indirectBindingTable); + const char* rhsName = this->targetName(rhsAtom, indirectBindingTable); + return (strcmp(thisName, rhsName) == 0); +} + +template <typename A> +ld::Atom::Scope NonLazyPointerSection<A>::scopeAtAddress(Parser<A>& parser, pint_t addr) +{ + const macho_section<P>* sect = this->machoSection(); + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(addr, sect); + if ( symIndex == INDIRECT_SYMBOL_LOCAL) + return ld::Atom::scopeTranslationUnit; + else + return ld::Atom::scopeLinkageUnit; +} + + +template <typename A> +const uint8_t* CFStringSection<A>::targetContent(const class Atom<A>* atom, const ld::IndirectBindingTable& ind, + ContentType* ct, unsigned int* count) +{ + *ct = contentUnknown; + for (ld::Fixup::iterator fit=atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + const ld::Atom* targetAtom = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + // ignore reference to ___CFConstantStringClassReference + // we are just looking for reference to backing string data + assert(fit->offsetInAtom == 0); + assert(strcmp(fit->u.name, "___CFConstantStringClassReference") == 0); + break; + case ld::Fixup::bindingDirectlyBound: + case ld::Fixup::bindingByContentBound: + targetAtom = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + targetAtom = ind.indirectAtom(fit->u.bindingIndex); + break; + default: + assert(0 && "bad binding type"); + } + assert(targetAtom != NULL); + const Atom<A>* target = dynamic_cast<const Atom<A>*>(targetAtom); + if ( targetAtom->section().type() == ld::Section::typeCString ) { + *ct = contentUTF8; + *count = targetAtom->size(); + } + else if ( targetAtom->section().type() == ld::Section::typeUTF16Strings ) { + *ct = contentUTF16; + *count = (targetAtom->size()+1)/2; // round up incase of buggy compiler that has only one trailing zero byte + } + assert(target != NULL); + return target->contentPointer(); + } + assert(0); + return NULL; +} + +template <typename A> +unsigned long CFStringSection<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + // base hash of CFString on hash of cstring it wraps + ContentType cType; + unsigned long hash; + unsigned int charCount; + const uint8_t* content = this->targetContent(atom, ind, &cType, &charCount); + switch ( cType ) { + case contentUTF8: + hash = 9408; + for (const char* s = (char*)content; *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; + case contentUTF16: + hash = 407955; + --charCount; // don't add last 0x0000 to hash because some buggy compilers only have trailing single byte + for (const uint16_t* s = (uint16_t*)content; charCount > 0; ++s, --charCount) { + hash = hash * 1025 + *s; + } + return hash; + case contentUnknown: + return 0; + } + return 0; +} + + +template <typename A> +bool CFStringSection<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + if ( atom == &rhs ) + return true; + if ( rhs.section().type() != ld::Section::typeCFString) + return false; + assert(this->type() == rhs.section().type()); + assert(strcmp(this->sectionName(), "__cfstring") == 0); + + ContentType thisType; + unsigned int charCount; + const uint8_t* cstringContent = this->targetContent(atom, indirectBindingTable, &thisType, &charCount); + ContentType rhsType; + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + unsigned int rhsCharCount; + const uint8_t* rhsStringContent = this->targetContent(rhsAtom, indirectBindingTable, &rhsType, &rhsCharCount); + + if ( thisType != rhsType ) + return false; + + // no need to compare content of pointers are already the same + if ( cstringContent == rhsStringContent ) + return true; + + // no need to compare content if size is different + if ( charCount != rhsCharCount ) + return false; + + switch ( thisType ) { + case contentUTF8: + return (strcmp((char*)cstringContent, (char*)rhsStringContent) == 0); + case contentUTF16: + { + const uint16_t* cstringContent16 = (uint16_t*)cstringContent; + const uint16_t* rhsStringContent16 = (uint16_t*)rhsStringContent; + for (unsigned int i = 0; i < charCount; ++i) { + if ( cstringContent16[i] != rhsStringContent16[i] ) + return false; + } + return true; + } + case contentUnknown: + return false; + } + return false; +} + + +template <typename A> +typename A::P::uint_t ObjC1ClassSection<A>::elementSizeAtAddress(pint_t addr) +{ + // nominal size for each class is 48 bytes, but sometimes the compiler + // over aligns and there is padding after class data + const macho_section<P>* sct = this->machoSection(); + uint32_t align = 1 << sct->align(); + uint32_t size = ((12 * sizeof(pint_t)) + align-1) & (-align); + return size; +} + +template <typename A> +const char* ObjC1ClassSection<A>::unlabeledAtomName(Parser<A>& parser, pint_t addr) +{ + // 8-bytes into class object is pointer to class name + const macho_section<P>* sct = this->machoSection(); + uint32_t classObjcFileOffset = sct->offset() - sct->addr() + addr; + const uint8_t* mappedFileContent = this->file().fileContent(); + pint_t nameAddr = P::getP(*((pint_t*)(mappedFileContent+classObjcFileOffset+2*sizeof(pint_t)))); + + // find section containing string address to get string bytes + const macho_section<P>* const sections = parser.firstMachOSection(); + const uint32_t sectionCount = parser.machOSectionCount(); + for (uint32_t i=0; i < sectionCount; ++i) { + const macho_section<P>* aSect = §ions[i]; + if ( (aSect->addr() <= nameAddr) && (nameAddr < (aSect->addr()+aSect->size())) ) { + assert((aSect->flags() & SECTION_TYPE) == S_CSTRING_LITERALS); + uint32_t nameFileOffset = aSect->offset() - aSect->addr() + nameAddr; + const char* name = (char*)mappedFileContent + nameFileOffset; + // spin through symbol table to find absolute symbol corresponding to this class + for (uint32_t s=0; s < parser.symbolCount(); ++s) { + const macho_nlist<P>& sym = parser.symbolFromIndex(s); + if ( (sym.n_type() & N_TYPE) != N_ABS ) + continue; + const char* absName = parser.nameFromSymbol(sym); + if ( strncmp(absName, ".objc_class_name_", 17) == 0 ) { + if ( strcmp(&absName[17], name) == 0 ) + return absName; + } + } + assert(0 && "obj class name not found in symbol table"); + } + } + assert(0 && "obj class name not found"); + return "unknown objc class"; +} + + +template <typename A> +const char* ObjC2ClassRefsSection<A>::targetClassName(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->fixupCount() == 1); + ld::Fixup::iterator fit = atom->fixupsBegin(); + const char* className = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + className = fit->u.name; + break; + case ld::Fixup::bindingDirectlyBound: + case ld::Fixup::bindingByContentBound: + className = fit->u.target->name(); + break; + case ld::Fixup::bindingsIndirectlyBound: + className = ind.indirectName(fit->u.bindingIndex); + break; + default: + assert(0 && "unsupported binding in objc2 class ref section"); + } + assert(className != NULL); + return className; +} + + +template <typename A> +unsigned long ObjC2ClassRefsSection<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 978; + for (const char* s = targetClassName(atom, ind); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template <typename A> +bool ObjC2ClassRefsSection<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + assert(this->type() == rhs.section().type()); + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + const char* thisClassName = targetClassName(atom, indirectBindingTable); + const char* rhsClassName = targetClassName(rhsAtom, indirectBindingTable); + return (strcmp(thisClassName, rhsClassName) == 0); +} + + +template <typename A> +const char* Objc1ClassReferences<A>::targetCString(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->fixupCount() == 2); + ld::Fixup::iterator fit = atom->fixupsBegin(); + if ( fit->kind == ld::Fixup::kindSetTargetAddress ) + ++fit; + const ld::Atom* targetAtom = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByContentBound: + targetAtom = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + targetAtom = ind.indirectAtom(fit->u.bindingIndex); + if ( targetAtom == NULL ) { + fprintf(stderr, "missing target named %s\n", ind.indirectName(fit->u.bindingIndex)); + } + break; + default: + assert(0); + } + assert(targetAtom != NULL); + const Atom<A>* target = dynamic_cast<const Atom<A>*>(targetAtom); + assert(target != NULL); + return (char*)target->contentPointer(); +} + + +template <typename A> +const char* PointerToCStringSection<A>::targetCString(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->fixupCount() == 1); + ld::Fixup::iterator fit = atom->fixupsBegin(); + const ld::Atom* targetAtom = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByContentBound: + targetAtom = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + targetAtom = ind.indirectAtom(fit->u.bindingIndex); + break; + default: + assert(0); + } + assert(targetAtom != NULL); + const Atom<A>* target = dynamic_cast<const Atom<A>*>(targetAtom); + assert(target != NULL); + return (char*)target->contentPointer(); +} + +template <typename A> +unsigned long PointerToCStringSection<A>::contentHash(const class Atom<A>* atom, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + // make hash from section name and target cstring name + unsigned long hash = 123; + for (const char* s = this->sectionName(); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + for (const char* s = this->targetCString(atom, indirectBindingTable); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template <typename A> +bool PointerToCStringSection<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + assert(this->type() == rhs.section().type()); + // there can be pointers-to-cstrings in different section names + // we only want to coalesce in same section name + if ( *this != rhs.section() ) + return false; + + // get string content for this + const char* cstringContent = this->targetCString(atom, indirectBindingTable); + const Atom<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&rhs); + assert(rhsAtom != NULL); + const char* rhsCstringContent = this->targetCString(rhsAtom, indirectBindingTable); + + assert(cstringContent != NULL); + assert(rhsCstringContent != NULL); + return (strcmp(cstringContent, rhsCstringContent) == 0); +} + + + +template <typename A> +unsigned long UTF16StringSection<A>::contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const +{ + unsigned long hash = 5381; + const uint16_t* stringContent = (uint16_t*)atom->contentPointer(); + // some buggy compilers end utf16 data with single byte, so don't use last word in hash computation + unsigned int count = (atom->size()/2) - 1; + for (const uint16_t* s = stringContent; count > 0; ++s, --count) { + hash = hash * 33 + *s; + } + return hash; +} + +template <typename A> +bool UTF16StringSection<A>::canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const +{ + if ( rhs.section().type() != ld::Section::typeUTF16Strings ) + return false; + assert(0); + return false; +} + + + + + + + +template <> +uint32_t Section<x86_64>::x86_64PcRelOffset(uint8_t r_type) +{ + switch ( r_type ) { + case X86_64_RELOC_SIGNED: + return 4; + case X86_64_RELOC_SIGNED_1: + return 5; + case X86_64_RELOC_SIGNED_2: + return 6; + case X86_64_RELOC_SIGNED_4: + return 8; + } + return 0; +} + + +template <> +bool Section<x86_64>::addRelocFixup(class Parser<x86_64>& parser, const macho_relocation_info<P>* reloc) +{ + const macho_section<P>* sect = this->machoSection(); + uint64_t srcAddr = sect->addr() + reloc->r_address(); + Parser<x86_64>::SourceLocation src; + Parser<x86_64>::TargetDesc target; + Parser<x86_64>::TargetDesc toTarget; + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint64_t contentValue = 0; + const macho_relocation_info<x86_64::P>* nextReloc = &reloc[1]; + bool result = false; + bool useDirectBinding; + switch ( reloc->r_length() ) { + case 0: + contentValue = *fixUpPtr; + break; + case 1: + contentValue = (int64_t)(int16_t)E::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + contentValue = (int64_t)(int32_t)E::get32(*((uint32_t*)fixUpPtr)); + break; + case 3: + contentValue = E::get64(*((uint64_t*)fixUpPtr)); + break; + } + target.atom = NULL; + target.name = NULL; + target.weakImport = false; + target.addend = 0; + if ( reloc->r_extern() ) { + const macho_nlist<P>& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(sym)[0] == 'L')) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + target.addend += contentValue; + } + else { + target.name = parser.nameFromSymbol(sym); + target.weakImport = parser.weakImportFromSymbol(sym); + target.addend = contentValue; + } + // cfstrings should always use direct reference to backing store + if ( (this->type() == ld::Section::typeCFString) && (src.offsetInAtom != 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + target.addend = contentValue; + } + } + else { + if ( reloc->r_pcrel() ) + contentValue += srcAddr + x86_64PcRelOffset(reloc->r_type()); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + switch ( reloc->r_type() ) { + case X86_64_RELOC_UNSIGNED: + if ( reloc->r_pcrel() ) + throw "pcrel and X86_64_RELOC_UNSIGNED not supported"; + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "length < 2 and X86_64_RELOC_UNSIGNED not supported"; + case 2: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case 3: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian64, target); + break; + } + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_SIGNED* not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_SIGNED* not supported"; + switch ( reloc->r_type() ) { + case X86_64_RELOC_SIGNED: + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32, target); + break; + case X86_64_RELOC_SIGNED_1: + if ( reloc->r_extern() ) + target.addend += 1; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32_1, target); + break; + case X86_64_RELOC_SIGNED_2: + if ( reloc->r_extern() ) + target.addend += 2; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32_2, target); + break; + case X86_64_RELOC_SIGNED_4: + if ( reloc->r_extern() ) + target.addend += 4; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32_4, target); + break; + } + break; + case X86_64_RELOC_BRANCH: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_BRANCH not supported"; + switch ( reloc->r_length() ) { + case 2: + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreX86BranchPCRel32, target); + } + break; + case 0: + parser.addFixups(src, ld::Fixup::kindStoreX86BranchPCRel8, target); + break; + default: + throwf("length=%d and X86_64_RELOC_BRANCH not supported", reloc->r_length()); + } + break; + case X86_64_RELOC_GOT: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT not supported"; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32GOT, target); + break; + case X86_64_RELOC_GOT_LOAD: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_GOT_LOAD not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_GOT_LOAD not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_GOT_LOAD not supported"; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32GOTLoad, target); + break; + case X86_64_RELOC_SUBTRACTOR: + if ( reloc->r_pcrel() ) + throw "X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( reloc->r_length() < 2 ) + throw "X86_64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; + if ( !reloc->r_extern() ) + throw "X86_64_RELOC_SUBTRACTOR must have r_extern=1"; + if ( nextReloc->r_type() != X86_64_RELOC_UNSIGNED ) + throw "X86_64_RELOC_SUBTRACTOR must be followed by X86_64_RELOC_UNSIGNED"; + result = true; + if ( nextReloc->r_pcrel() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( nextReloc->r_length() != reloc->r_length() ) + throw "X86_64_RELOC_UNSIGNED following a X86_64_RELOC_SUBTRACTOR must have same r_length"; + if ( nextReloc->r_extern() ) { + const macho_nlist<P>& sym = parser.symbolFromIndex(nextReloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(sym)[0] == 'L')) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), toTarget); + toTarget.addend = contentValue; + useDirectBinding = true; + } + else { + toTarget.name = parser.nameFromSymbol(sym); + toTarget.weakImport = parser.weakImportFromSymbol(sym); + toTarget.addend = contentValue; + useDirectBinding = false; + } + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, nextReloc->r_symbolnum(), toTarget); + useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit); + } + if ( useDirectBinding ) + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); + else + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.weakImport, toTarget.name); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, toTarget.addend); + if ( target.atom == NULL ) + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, false, target.name); + else + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, target.atom); + if ( reloc->r_length() == 2 ) + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32); + else + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian64); + break; + case X86_64_RELOC_TLV: + if ( ! reloc->r_extern() ) + throw "not extern and X86_64_RELOC_TLV not supported"; + if ( ! reloc->r_pcrel() ) + throw "not pcrel and X86_64_RELOC_TLV not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and X86_64_RELOC_TLV not supported"; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32TLVLoad, target); + break; + default: + throwf("unknown relocation type %d", reloc->r_type()); + } + return result; +} + + + +template <> +bool Section<x86>::addRelocFixup(class Parser<x86>& parser, const macho_relocation_info<P>* reloc) +{ + const macho_section<P>* sect = this->machoSection(); + uint32_t srcAddr; + const uint8_t* fixUpPtr; + uint32_t contentValue = 0; + ld::Fixup::Kind kind = ld::Fixup::kindNone; + Parser<x86>::SourceLocation src; + Parser<x86>::TargetDesc target; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + switch ( reloc->r_type() ) { + case GENERIC_RELOC_VANILLA: + switch ( reloc->r_length() ) { + case 0: + contentValue = (int32_t)(int8_t)*fixUpPtr; + if ( reloc->r_pcrel() ) { + kind = ld::Fixup::kindStoreX86BranchPCRel8; + contentValue += srcAddr + sizeof(uint8_t); + } + else + throw "r_length=0 and r_pcrel=0 not supported"; + break; + case 1: + contentValue = (int32_t)(int16_t)E::get16(*((uint16_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + kind = ld::Fixup::kindStoreX86PCRel16; + contentValue += srcAddr + sizeof(uint16_t); + } + else + kind = ld::Fixup::kindStoreLittleEndian16; + break; + case 2: + contentValue = E::get32(*((uint32_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + kind = ld::Fixup::kindStoreX86BranchPCRel32; + contentValue += srcAddr + sizeof(uint32_t); + } + else + kind = ld::Fixup::kindStoreLittleEndian32; + break; + case 3: + throw "r_length=3 not supported"; + } + if ( reloc->r_extern() ) { + target.atom = NULL; + const macho_nlist<P>& targetSymbol = parser.symbolFromIndex(reloc->r_symbolnum()); + target.name = parser.nameFromSymbol(targetSymbol); + target.weakImport = parser.weakImportFromSymbol(targetSymbol); + target.addend = contentValue; + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + if ( (kind == ld::Fixup::kindStoreX86BranchPCRel32) && (target.name != NULL) ) { + if ( strncmp(target.name, "___dtrace_probe$", 16) == 0 ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + return false; + } + else if ( strncmp(target.name, "___dtrace_isenabled$", 20) == 0 ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreX86DtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + return false; + } + } + parser.addFixups(src, kind, target); + return false; + break; + case GENERIC_RLEOC_TLV: + { + if ( !reloc->r_extern() ) + throw "r_extern=0 and r_type=GENERIC_RLEOC_TLV not supported"; + if ( reloc->r_length() != 2 ) + throw "r_length!=2 and r_type=GENERIC_RLEOC_TLV not supported"; + const macho_nlist<P>& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && ((sym.n_type() & N_EXT) == 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + } + else { + target.atom = NULL; + target.name = parser.nameFromSymbol(sym); + target.weakImport = parser.weakImportFromSymbol(sym); + } + target.addend = (int64_t)(int32_t)E::get32(*((uint32_t*)fixUpPtr)); + if ( reloc->r_pcrel() ) { + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32TLVLoad, target); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreX86Abs32TLVLoad, target); + } + return false; + } + break; + default: + throwf("unsupported i386 relocation type (%d)", reloc->r_type()); + } + } + else { + // scattered relocation + const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; + srcAddr = sect->addr() + sreloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + fixUpPtr = file().fileContent() + sect->offset() + sreloc->r_address(); + uint32_t relocValue = sreloc->r_value(); + bool result = false; + // file format allows pair to be scattered or not + const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1]; + const macho_relocation_info<P>* nextReloc = &reloc[1]; + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; // iterator should skip next reloc, since we've consumed it here + } + } + else { + if ( nextSReloc->r_type() == GENERIC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + } + } + switch (sreloc->r_type()) { + case GENERIC_RELOC_VANILLA: + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + target.atom = parser.findAtomByAddress(relocValue); + if ( sreloc->r_pcrel() ) { + switch ( sreloc->r_length() ) { + case 0: + contentValue = srcAddr + 1 + *fixUpPtr; + target.addend = contentValue - relocValue; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel8, target); + break; + case 1: + contentValue = srcAddr + 2 + LittleEndian::get16(*((uint16_t*)fixUpPtr)); + target.addend = contentValue - relocValue; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel16, target); + break; + case 2: + contentValue = srcAddr + 4 + LittleEndian::get32(*((uint32_t*)fixUpPtr)); + target.addend = contentValue - relocValue; + parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32, target); + break; + case 3: + throw "unsupported r_length=3 for scattered pc-rel vanilla reloc"; + break; + } + } + else { + if ( sreloc->r_length() != 2 ) + throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length()); + contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + target.addend = contentValue - target.atom->objectAddress(); + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + } + break; + case GENERIC_RELOC_SECTDIFF: + case GENERIC_RELOC_LOCAL_SECTDIFF: + { + if ( !nextRelocIsPair ) + throw "GENERIC_RELOC_SECTDIFF missing following pair"; + switch ( sreloc->r_length() ) { + case 0: + case 3: + throw "bad length for GENERIC_RELOC_SECTDIFF"; + case 1: + contentValue = (int32_t)(int16_t)LittleEndian::get16(*((uint16_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreLittleEndian16; + break; + case 2: + contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreLittleEndian32; + break; + } + Atom<x86>* fromAtom = parser.findAtomByAddress(nextRelocValue); + uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; + parser.findTargetFromAddress(sreloc->r_value(), target); + // check for addend encoded in the section content + int32_t addend = contentValue - (sreloc->r_value() - nextRelocValue); + if ( addend < 0 ) { + // switch binding base on coalescing + if ( target.atom == NULL ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.name); + } + else if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, target.atom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.atom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, target.addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom-addend); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + else { + // switch binding base on coalescing + if ( target.atom == NULL ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.name); + } + else if ( target.atom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, target.atom); + } + else if ( (target.atom->combine() == ld::Atom::combineByNameAndContent) || (target.atom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, target.atom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, target.atom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, target.addend+addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + } + break; + } + return result; + } +} + + + +// +// ppc and ppc64 both use the same relocations, so process them in one common routine +// +template <typename A> +bool Section<A>::addRelocFixup_powerpc(class Parser<A>& parser, + const macho_relocation_info<typename A::P>* reloc) +{ + const macho_section<P>* sect = this->machoSection(); + bool result = false; + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + int16_t lowBits; + pint_t contentValue = 0; + typename Parser<A>::SourceLocation src; + typename Parser<A>::TargetDesc target; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + const macho_relocation_info<P>* nextReloc = &reloc[1]; + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != PPC_RELOC_PAIR ) + instruction = BigEndian::get32(*fixUpPtr); + if ( reloc->r_extern() ) { + target.atom = NULL; + const macho_nlist<P>& targetSymbol = parser.symbolFromIndex(reloc->r_symbolnum()); + target.name = parser.nameFromSymbol(targetSymbol); + target.weakImport = parser.weakImportFromSymbol(targetSymbol); + } + switch ( reloc->r_type() ) { + case PPC_RELOC_BR24: + assert((instruction & 0x4C000000) == 0x48000000); + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + // special case "calls" for dtrace + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStorePPCDtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStorePPCDtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + } + break; + case PPC_RELOC_BR14: + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); + break; + case PPC_RELOC_PAIR: + // skip, processed by a previous look ahead + break; + case PPC_RELOC_LO16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_LO16 missing following pair"; + result = true; + lowBits = (instruction & 0x0000FFFF); + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); + break; + case PPC_RELOC_LO14: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_LO14 missing following pair"; + result = true; + lowBits = (instruction & 0xFFFC); + dstAddr = (nextReloc->r_address() << 16) + ((uint32_t)lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); + break; + case PPC_RELOC_HI16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_HI16 missing following pair"; + result = true; + lowBits = (nextReloc->r_address() & 0xFFFF); + dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); + break; + case PPC_RELOC_HA16: + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_HA16 missing following pair"; + result = true; + lowBits = (nextReloc->r_address() & 0x0000FFFF); + dstAddr = ((instruction & 0xFFFF) << 16) + (int32_t)lowBits; + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); + break; + case PPC_RELOC_VANILLA: + contentValue = P::getP(*((pint_t*)fixUpPtr)); + if ( reloc->r_extern() ) { + target.addend = contentValue; + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "bad r_length in PPC_RELOC_VANILLA"; + case 2: + parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); + break; + case 3: + parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); + break; + } + break; + case PPC_RELOC_JBSR: + // this is from -mlong-branch codegen. We ignore the jump island and make reference to the real target + if ( nextReloc->r_type() != PPC_RELOC_PAIR ) + throw "PPC_RELOC_JBSR missing following pair"; + if ( !parser._hasLongBranchStubs ) + warning("object file compiled with -mlong-branch which is no longer needed. " + "To remove this warning, recompile without -mlong-branch: %s", parser._path); + parser._hasLongBranchStubs = true; + result = true; + if ( reloc->r_extern() ) { + throw "PPC_RELOC_JBSR should not be using an external relocation"; + } + parser.findTargetFromAddressAndSectionNum(nextReloc->r_address(), reloc->r_symbolnum(), target); + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + break; + default: + warning("unknown relocation type %d", reloc->r_type()); + } + } + else { + const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; + // file format allows pair to be scattered or not + const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1]; + const macho_relocation_info<P>* nextReloc = &reloc[1]; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + sreloc->r_address()); + instruction = BigEndian::get32(*fixUpPtr); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + typename Parser<A>::TargetDesc picBase; + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == PPC_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch ( sreloc->r_type() ) { + case PPC_RELOC_VANILLA: + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + target.atom = parser.findAtomByAddress(sreloc->r_value()); + switch ( sreloc->r_length() ) { + case 0: + case 1: + throw "unsuppored r_length < 2 for scattered PPC_RELOC_VANILLA"; + case 2: + contentValue = BigEndian::get32(*(uint32_t*)fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreBigEndian32, target); + break; + case 3: + contentValue = BigEndian::get64(*(uint64_t*)fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreBigEndian64, target); + break; + } + break; + case PPC_RELOC_BR14: + displacement = (instruction & 0x0000FFFC); + if ( (displacement & 0x00008000) != 0 ) + displacement |= 0xFFFF0000; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (srcAddr + displacement) - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStorePPCBranch14, target); + break; + case PPC_RELOC_BR24: + assert((instruction & 0x4C000000) == 0x48000000); + displacement = (instruction & 0x03FFFFFC); + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (srcAddr + displacement) - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStorePPCBranch24, target); + break; + case PPC_RELOC_LO16_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO16_SECTDIFF missing following pair"; + lowBits = (instruction & 0xFFFF); + dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicLow16, target, picBase); + break; + case PPC_RELOC_LO14_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO14_SECTDIFF missing following pair"; + lowBits = (instruction & 0xFFFC); + dstAddr = nextRelocValue + ((nextRelocAddress << 16) | ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicLow14, target, picBase); + break; + case PPC_RELOC_HA16_SECTDIFF: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HA16_SECTDIFF missing following pair"; + lowBits = (nextRelocAddress & 0x0000FFFF); + dstAddr = nextRelocValue + (((instruction & 0x0000FFFF) << 16) + (int32_t)lowBits); + parser.findTargetFromAddress(sreloc->r_value(), target); + if ( target.atom != NULL ) + target.addend = dstAddr - target.atom->_objAddress; + picBase.atom = parser.findAtomByAddress(nextRelocValue); + picBase.addend = nextRelocValue - picBase.atom->_objAddress; + picBase.weakImport = false; + picBase.name = NULL; + parser.addFixups(src, ld::Fixup::kindStorePPCPicHigh16AddLow, target, picBase); + break; + case PPC_RELOC_LO14: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO14 missing following pair"; + lowBits = (instruction & 0xFFFC); + dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow14, target); + break; + case PPC_RELOC_LO16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_LO16 missing following pair"; + lowBits = (instruction & 0xFFFF); + dstAddr = ((nextRelocAddress << 16) + ((uint32_t)lowBits & 0x0000FFFF)); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsLow16, target); + break; + case PPC_RELOC_HA16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HA16 missing following pair"; + lowBits = (nextRelocAddress & 0xFFFF); + dstAddr = (((instruction & 0xFFFF) << 16) + (int32_t)lowBits); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16AddLow, target); + break; + case PPC_RELOC_HI16: + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_HI16 missing following pair"; + lowBits = (nextRelocAddress & 0xFFFF); + dstAddr = ((instruction & 0xFFFF) << 16) | (lowBits & 0x0000FFFF); + parser.findTargetFromAddress(sreloc->r_value(), dstAddr, target); + parser.addFixups(src, ld::Fixup::kindStorePPCAbsHigh16, target); + break; + case PPC_RELOC_SECTDIFF: + case PPC_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) + throw "PPC_RELOC_SECTDIFF missing following pair"; + ld::Fixup::Kind kind = ld::Fixup::kindNone; + switch ( sreloc->r_length() ) { + case 0: + throw "bad length for PPC_RELOC_SECTDIFF"; + case 1: + contentValue = (int32_t)(int16_t)BigEndian::get16(*((uint16_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian16; + break; + case 2: + contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian32; + break; + case 3: + contentValue = BigEndian::get64(*((uint64_t*)fixUpPtr)); + kind = ld::Fixup::kindStoreBigEndian64; + break; + break; + } + Atom<A>* fromAtom = parser.findAtomByAddress(nextRelocValue); + Atom<A>* targetAtom = parser.findAtomByAddress(sreloc->r_value()); + uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; + uint32_t offsetInTarget = sreloc->r_value() - targetAtom->_objAddress; + // check for addend encoded in the section content + int32_t addend = contentValue - (sreloc->r_value() - nextRelocValue); + if ( addend < 0 ) { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom-addend); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + else { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget+addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + parser.addFixup(src, ld::Fixup::k5of5, kind); + } + } + break; + case PPC_RELOC_PAIR: + break; + case PPC_RELOC_HI16_SECTDIFF: + warning("unexpected scattered relocation type PPC_RELOC_HI16_SECTDIFF"); + break; + default: + warning("unknown scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + + +template <> +bool Section<ppc>::addRelocFixup(class Parser<ppc>& parser, const macho_relocation_info<P>* reloc) +{ + return addRelocFixup_powerpc(parser, reloc); +} + + +template <> +bool Section<ppc64>::addRelocFixup(class Parser<ppc64>& parser, const macho_relocation_info<P>* reloc) +{ + return addRelocFixup_powerpc(parser, reloc); +} + + + +template <> +bool Section<arm>::addRelocFixup(class Parser<arm>& parser, const macho_relocation_info<P>* reloc) +{ + const macho_section<P>* sect = this->machoSection(); + bool result = false; + uint32_t srcAddr; + uint32_t dstAddr; + uint32_t* fixUpPtr; + int32_t displacement = 0; + uint32_t instruction = 0; + pint_t contentValue = 0; + Parser<arm>::SourceLocation src; + Parser<arm>::TargetDesc target; + const macho_relocation_info<P>* nextReloc; + + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + bool externSymbolIsThumbDef = false; + srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + reloc->r_address()); + if ( reloc->r_type() != ARM_RELOC_PAIR ) + instruction = LittleEndian::get32(*fixUpPtr); + if ( reloc->r_extern() ) { + target.atom = NULL; + const macho_nlist<P>& targetSymbol = parser.symbolFromIndex(reloc->r_symbolnum()); + target.name = parser.nameFromSymbol(targetSymbol); + target.weakImport = parser.weakImportFromSymbol(targetSymbol); + if ( ((targetSymbol.n_type() & N_TYPE) == N_SECT) && (targetSymbol.n_desc() & N_ARM_THUMB_DEF) ) + externSymbolIsThumbDef = true; + } + switch ( reloc->r_type() ) { + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + if ( reloc->r_extern() ) { + target.addend = srcAddr + displacement; + if ( externSymbolIsThumbDef ) + target.addend &= -2; // remove thumb bit + } + else { + dstAddr = srcAddr + displacement; + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + // special case "calls" for dtrace + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreARMDtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreARMDtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreARMBranch24, target); + } + break; + case ARM_THUMB_RELOC_BR22: + // thumb2 added two more bits to displacement, complicating the displacement decoding + { + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + if ( s ) + sdis |= 0xFE000000; + displacement = sdis; + } + // The pc added will be +4 from the pc + displacement += 4; + // If the instruction was blx, force the low 2 bits to be clear + dstAddr = srcAddr + displacement; + if ((instruction & 0xF8000000) == 0xE8000000) + dstAddr &= 0xFFFFFFFC; + + if ( reloc->r_extern() ) { + target.addend = dstAddr; + } + else { + parser.findTargetFromAddressAndSectionNum(dstAddr, reloc->r_symbolnum(), target); + } + // special case "calls" for dtrace + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreThumbDtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, + ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + parser.addFixups(src, ld::Fixup::kindStoreThumbBranch22, target); + } + break; + case ARM_RELOC_VANILLA: + if ( reloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + contentValue = LittleEndian::get32(*fixUpPtr); + if ( reloc->r_extern() ) { + target.addend = contentValue; + if ( externSymbolIsThumbDef ) + target.addend &= -2; // remove thumb bit + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + // possible non-extern relocation turned into by-name ref because target is a weak-def + if ( target.atom != NULL ) { + if ( target.atom->isThumb() ) + target.addend &= -2; // remove thumb bit + // if reference to LSDA, add group subordinate fixup + if ( target.atom->contentType() == ld::Atom::typeLSDA ) { + Parser<arm>::SourceLocation src2; + src2.atom = src.atom; + src2.offsetInAtom = 0; + parser.addFixup(src2, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, target.atom); + } + } + } + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case ARM_THUMB_32BIT_BRANCH: + // silently ignore old unnecessary reloc + break; + case ARM_RELOC_HALF: + nextReloc = &reloc[1]; + if ( nextReloc->r_type() == ARM_RELOC_PAIR ) { + uint32_t instruction16; + uint32_t other16 = (nextReloc->r_address() & 0xFFFF); + bool isThumb; + if ( reloc->r_length() & 2 ) { + isThumb = true; + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + instruction16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + } + else { + isThumb = false; + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + instruction16 = (imm4 << 12) | imm12; + } + if ( reloc->r_length() & 1 ) { + // high 16 + dstAddr = ((instruction16 << 16) | other16); + parser.findTargetFromAddress(dstAddr, target); + parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16), target); + } + else { + // low 16 + dstAddr = (other16 << 16) | instruction16; + parser.findTargetFromAddress(dstAddr, target); + parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16), target); + } + result = true; + } + else + throw "for ARM_RELOC_HALF, next reloc is not ARM_RELOC_PAIR"; + break; + default: + throwf("unknown relocation type %d", reloc->r_type()); + break; + } + } + else { + const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc; + // file format allows pair to be scattered or not + const macho_scattered_relocation_info<P>* nextSReloc = &sreloc[1]; + nextReloc = &reloc[1]; + srcAddr = sect->addr() + sreloc->r_address(); + dstAddr = sreloc->r_value(); + fixUpPtr = (uint32_t*)(file().fileContent() + sect->offset() + sreloc->r_address()); + instruction = LittleEndian::get32(*fixUpPtr); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + bool nextRelocIsPair = false; + uint32_t nextRelocAddress = 0; + uint32_t nextRelocValue = 0; + if ( (nextReloc->r_address() & R_SCATTERED) == 0 ) { + if ( nextReloc->r_type() == ARM_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextReloc->r_address(); + result = true; + } + } + else { + if ( nextSReloc->r_type() == ARM_RELOC_PAIR ) { + nextRelocIsPair = true; + nextRelocAddress = nextSReloc->r_address(); + nextRelocValue = nextSReloc->r_value(); + result = true; + } + } + switch ( sreloc->r_type() ) { + case ARM_RELOC_VANILLA: + // with a scattered relocation we get both the target (sreloc->r_value()) and the target+offset (*fixUpPtr) + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_VANILLA"; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + contentValue = LittleEndian::get32(*fixUpPtr); + target.addend = contentValue - target.atom->_objAddress; + if ( target.atom->isThumb() ) + target.addend &= -2; // remove thumb bit + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case ARM_RELOC_BR24: + // Sign-extend displacement + displacement = (instruction & 0x00FFFFFF) << 2; + if ( (displacement & 0x02000000) != 0 ) + displacement |= 0xFC000000; + // The pc added will be +8 from the pc + displacement += 8; + // If this is BLX add H << 1 + if ((instruction & 0xFE000000) == 0xFA000000) + displacement += ((instruction & 0x01000000) >> 23); + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = (int64_t)(srcAddr + displacement) - (int64_t)(target.atom->_objAddress); + parser.addFixups(src, ld::Fixup::kindStoreARMBranch24, target); + break; + case ARM_THUMB_RELOC_BR22: + // thumb2 added two more bits to displacement, complicating the displacement decoding + { + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + if ( s ) + sdis |= 0xFE000000; + displacement = sdis; + } + // The pc added will be +4 from the pc + displacement += 4; + dstAddr = srcAddr+displacement; + // If the instruction was blx, force the low 2 bits to be clear + if ((instruction & 0xF8000000) == 0xE8000000) + dstAddr &= 0xFFFFFFFC; + target.atom = parser.findAtomByAddress(sreloc->r_value()); + target.addend = dstAddr - target.atom->_objAddress; + parser.addFixups(src, ld::Fixup::kindStoreThumbBranch22, target); + break; + case ARM_RELOC_SECTDIFF: + case ARM_RELOC_LOCAL_SECTDIFF: + { + if ( ! nextRelocIsPair ) + throw "ARM_RELOC_SECTDIFF missing following pair"; + if ( sreloc->r_length() != 2 ) + throw "bad length for ARM_RELOC_SECTDIFF"; + contentValue = LittleEndian::get32(*fixUpPtr); + Atom<arm>* fromAtom = parser.findAtomByAddress(nextRelocValue); + uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; + uint32_t offsetInTarget; + Atom<arm>* targetAtom = parser.findAtomByAddressOrLocalTargetOfStub(sreloc->r_value(), &offsetInTarget); + // check for addend encoded in the section content + int64_t addend = contentValue - (sreloc->r_value() - nextRelocValue); + if ( targetAtom->isThumb() ) + addend &= -2; // remove thumb bit + // if reference to LSDA, add group subordinate fixup + if ( targetAtom->contentType() == ld::Atom::typeLSDA ) { + Parser<arm>::SourceLocation src2; + src2.atom = src.atom; + src2.offsetInAtom = 0; + parser.addFixup(src2, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, targetAtom); + } + if ( addend < 0 ) { + // switch binding base on coalescing + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom-addend); + parser.addFixup(src, ld::Fixup::k5of5, ld::Fixup::kindStoreLittleEndian32); + } + else { + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, (uint32_t)(offsetInTarget+addend)); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + parser.addFixup(src, ld::Fixup::k5of5, ld::Fixup::kindStoreLittleEndian32); + } + } + break; + case ARM_RELOC_HALF_SECTDIFF: + if ( nextRelocIsPair ) { + instruction = LittleEndian::get32(*fixUpPtr); + Atom<arm>* fromAtom = parser.findAtomByAddress(nextRelocValue); + uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress; + Atom<arm>* targetAtom = parser.findAtomByAddress(sreloc->r_value()); + uint32_t offsetInTarget = sreloc->r_value() - targetAtom->_objAddress; + //if ( targetAtom->isThumb() ) + // addend &= -2; // remove thumb bit + uint32_t instruction16; + uint32_t other16 = (nextRelocAddress & 0xFFFF); + bool isThumb; + if ( sreloc->r_length() & 2 ) { + isThumb = true; + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + instruction16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + } + else { + isThumb = false; + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + instruction16 = (imm4 << 12) | imm12; + } + if ( sreloc->r_length() & 1 ) + dstAddr = ((instruction16 << 16) | other16); + else + dstAddr = (other16 << 16) | instruction16; + int32_t addend = dstAddr - (sreloc->r_value() - nextRelocValue); + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, (uint32_t)offsetInTarget+addend); + parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom); + parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom); + if ( sreloc->r_length() & 1 ) { + // high 16 + parser.addFixup(src, ld::Fixup::k5of5, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16)); + } + else { + // low 16 + parser.addFixup(src, ld::Fixup::k5of5, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16)); + } + result = true; + } + else + throw "ARM_RELOC_HALF_SECTDIFF reloc missing following pair"; + break; + case ARM_RELOC_HALF: + if ( nextRelocIsPair ) { + instruction = LittleEndian::get32(*fixUpPtr); + Atom<arm>* targetAtom = parser.findAtomByAddress(sreloc->r_value()); + uint32_t instruction16; + uint32_t other16 = (nextRelocAddress & 0xFFFF); + bool isThumb; + if ( sreloc->r_length() & 2 ) { + isThumb = true; + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + instruction16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + } + else { + isThumb = false; + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + instruction16 = (imm4 << 12) | imm12; + } + if ( sreloc->r_length() & 1 ) + dstAddr = ((instruction16 << 16) | other16); + else + dstAddr = (other16 << 16) | instruction16; + if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) { + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, targetAtom); + } + else if ( (targetAtom->combine() == ld::Atom::combineByNameAndContent) || (targetAtom->combine() == ld::Atom::combineByNameAndReferences) ) { + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, targetAtom); + } + else { + parser.addFixup(src, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, false, targetAtom->name()); + } + parser.addFixup(src, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, dstAddr - targetAtom->_objAddress); + if ( sreloc->r_length() & 1 ) { + // high 16 + parser.addFixup(src, ld::Fixup::k3of3, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16)); + } + else { + // low 16 + parser.addFixup(src, ld::Fixup::k3of3, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16)); + } + result = true; + } + else + throw "scattered ARM_RELOC_HALF reloc missing following pair"; + break; + default: + throwf("unknown ARM scattered relocation type %d", sreloc->r_type()); + } + } + return result; +} + + + + + +template <typename A> +bool ObjC1ClassSection<A>::addRelocFixup(class Parser<A>& parser, const macho_relocation_info<P>* reloc) +{ + // inherited + FixedSizeSection<A>::addRelocFixup(parser, reloc); + + assert(0 && "needs template specialization"); + return false; +} + +template <> +bool ObjC1ClassSection<x86>::addRelocFixup(class Parser<x86>& parser, const macho_relocation_info<x86::P>* reloc) +{ + // if this is the reloc for the super class name string, add implicit reference to super class + if ( ((reloc->r_address() & R_SCATTERED) == 0) && (reloc->r_type() == GENERIC_RELOC_VANILLA) ) { + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section<P>* sect = this->machoSection(); + Parser<x86>::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + if ( src.offsetInAtom == 4 ) { + Parser<x86>::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* superClassBaseName = (char*)stringTarget.atom->rawContentPointer(); + char* superClassName = new char[strlen(superClassBaseName) + 20]; + strcpy(superClassName, ".objc_class_name_"); + strcat(superClassName, superClassBaseName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, superClassName); + } + } + // inherited + return FixedSizeSection<x86>::addRelocFixup(parser, reloc); +} + +template <> +bool ObjC1ClassSection<ppc>::addRelocFixup(class Parser<ppc>& parser, const macho_relocation_info<ppc::P>* reloc) +{ + // if this is the reloc for the super class name string, add implicit reference to super class + if ( ((reloc->r_address() & R_SCATTERED) == 0) && (reloc->r_type() == PPC_RELOC_VANILLA) ) { + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section<P>* sect = this->machoSection(); + Parser<ppc>::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + if ( src.offsetInAtom == 4 ) { + Parser<ppc>::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* superClassBaseName = (char*)stringTarget.atom->rawContentPointer(); + char* superClassName = new char[strlen(superClassBaseName) + 20]; + strcpy(superClassName, ".objc_class_name_"); + strcat(superClassName, superClassBaseName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, superClassName); + } + } + + // inherited + return FixedSizeSection<ppc>::addRelocFixup(parser, reloc); +} + + + + +template <typename A> +bool Objc1ClassReferences<A>::addRelocFixup(class Parser<A>& parser, const macho_relocation_info<P>* reloc) +{ + // inherited + PointerToCStringSection<A>::addRelocFixup(parser, reloc); + + assert(0 && "needs template specialization"); + return false; +} + + +template <> +bool Objc1ClassReferences<ppc>::addRelocFixup(class Parser<ppc>& parser, const macho_relocation_info<ppc::P>* reloc) +{ + // add implict class refs, fixups not usable yet, so look at relocations + assert( (reloc->r_address() & R_SCATTERED) == 0 ); + assert( reloc->r_type() == PPC_RELOC_VANILLA ); + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section<P>* sect = this->machoSection(); + Parser<ppc>::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + Parser<ppc>::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = BigEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* baseClassName = (char*)stringTarget.atom->rawContentPointer(); + char* objcClassName = new char[strlen(baseClassName) + 20]; + strcpy(objcClassName, ".objc_class_name_"); + strcat(objcClassName, baseClassName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, objcClassName); + + // inherited + return PointerToCStringSection<ppc>::addRelocFixup(parser, reloc); +} + + +template <> +bool Objc1ClassReferences<x86>::addRelocFixup(class Parser<x86>& parser, const macho_relocation_info<x86::P>* reloc) +{ + // add implict class refs, fixups not usable yet, so look at relocations + assert( (reloc->r_address() & R_SCATTERED) == 0 ); + assert( reloc->r_type() == GENERIC_RELOC_VANILLA ); + assert( reloc->r_length() == 2 ); + assert( ! reloc->r_pcrel() ); + + const macho_section<P>* sect = this->machoSection(); + Parser<x86>::SourceLocation src; + uint32_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->objectAddress(); + Parser<x86>::TargetDesc stringTarget; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint32_t contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr)); + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), stringTarget); + + assert(stringTarget.atom != NULL); + assert(stringTarget.atom->contentType() == ld::Atom::typeCString); + const char* baseClassName = (char*)stringTarget.atom->rawContentPointer(); + char* objcClassName = new char[strlen(baseClassName) + 20]; + strcpy(objcClassName, ".objc_class_name_"); + strcat(objcClassName, baseClassName); + + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindSetTargetAddress, false, objcClassName); + + // inherited + return PointerToCStringSection<x86>::addRelocFixup(parser, reloc); +} + + +template <typename A> +void Section<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&) +{ + const macho_section<P>* sect = this->machoSection(); + const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(file().fileContent() + sect->reloff()); + const uint32_t relocCount = sect->nreloc(); + for (uint32_t r = 0; r < relocCount; ++r) { + try { + if ( this->addRelocFixup(parser, &relocs[r]) ) + ++r; // skip next + } + catch (const char* msg) { + throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg); + } + } + + // add follow-on fixups if .o file is missing .subsections_via_symbols + if ( this->addFollowOnFixups() ) { + Atom<A>* end = &_endAtoms[-1]; + for(Atom<A>* p = _beginAtoms; p < end; ++p) { + typename Parser<A>::SourceLocation src(p, 0); + Atom<A>* nextAtom = &p[1]; + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom); + } + } + else if ( this->type() == ld::Section::typeCode ) { + // if FDE broke text not at a symbol, use followOn to keep code together + Atom<A>* end = &_endAtoms[-1]; + for(Atom<A>* p = _beginAtoms; p < end; ++p) { + typename Parser<A>::SourceLocation src(p, 0); + Atom<A>* nextAtom = &p[1]; + if ( (p->symbolTableInclusion() == ld::Atom::symbolTableIn) && (nextAtom->symbolTableInclusion() == ld::Atom::symbolTableNotIn) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, nextAtom); + } + } + } + + // add follow-on fixups for aliases + if ( _hasAliases ) { + for(Atom<A>* p = _beginAtoms; p < _endAtoms; ++p) { + if ( p->isAlias() && ! this->addFollowOnFixups() ) { + Atom<A>* targetOfAlias = &p[1]; + assert(p < &_endAtoms[-1]); + assert(p->_objAddress == targetOfAlias->_objAddress); + typename Parser<A>::SourceLocation src(p, 0); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, targetOfAlias); + } + } + } +} + + + +// +// main function used by linker to instantiate ld::Files +// +ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts) +{ + switch ( opts.architecture ) { + case CPU_TYPE_X86_64: + if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ) + return mach_o::relocatable::Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_I386: + if ( mach_o::relocatable::Parser<x86>::validFile(fileContent) ) + return mach_o::relocatable::Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_ARM: + if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) + return mach_o::relocatable::Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC: + if ( mach_o::relocatable::Parser<ppc>::validFile(fileContent) ) + return mach_o::relocatable::Parser<ppc>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + case CPU_TYPE_POWERPC64: + if ( mach_o::relocatable::Parser<ppc64>::validFile(fileContent) ) + return mach_o::relocatable::Parser<ppc64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; + } + return NULL; +} + +// +// used by archive reader to validate member object file +// +bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts) +{ + switch ( opts.architecture ) { + case CPU_TYPE_X86_64: + return ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ); + case CPU_TYPE_I386: + return ( mach_o::relocatable::Parser<x86>::validFile(fileContent) ); + case CPU_TYPE_ARM: + return ( mach_o::relocatable::Parser<arm>::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); + case CPU_TYPE_POWERPC: + return ( mach_o::relocatable::Parser<ppc>::validFile(fileContent) ); + case CPU_TYPE_POWERPC64: + return ( mach_o::relocatable::Parser<ppc64>::validFile(fileContent) ); + } + return false; +} + +// +// used by linker to infer architecture when no -arch is on command line +// +bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult) +{ + if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ) { + *result = CPU_TYPE_X86_64; + *subResult = CPU_SUBTYPE_X86_64_ALL; + return true; + } + if ( mach_o::relocatable::Parser<x86>::validFile(fileContent) ) { + *result = CPU_TYPE_I386; + *subResult = CPU_SUBTYPE_X86_ALL; + return true; + } + if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, false, 0) ) { + *result = CPU_TYPE_ARM; + const macho_header<Pointer32<LittleEndian> >* header = (const macho_header<Pointer32<LittleEndian> >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( mach_o::relocatable::Parser<ppc>::validFile(fileContent) ) { + *result = CPU_TYPE_POWERPC; + const macho_header<Pointer32<BigEndian> >* header = (const macho_header<Pointer32<BigEndian> >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( mach_o::relocatable::Parser<ppc64>::validFile(fileContent) ) { + *result = CPU_TYPE_POWERPC64; + *subResult = CPU_SUBTYPE_POWERPC_ALL; + return true; + } + return false; +} + +// +// used by linker is error messages to describe bad .o file +// +const char* archName(const uint8_t* fileContent) +{ + if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ) { + return mach_o::relocatable::Parser<x86_64>::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser<x86>::validFile(fileContent) ) { + return mach_o::relocatable::Parser<x86>::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser<arm>::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser<ppc>::validFile(fileContent) ) { + return mach_o::relocatable::Parser<ppc>::fileKind(fileContent); + } + if ( mach_o::relocatable::Parser<ppc64>::validFile(fileContent) ) { + return mach_o::relocatable::Parser<ppc64>::fileKind(fileContent); + } + return NULL; +} + +// +// Used by archive reader when -ObjC option is specified +// +bool hasObjC2Categories(const uint8_t* fileContent) +{ + if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ) { + return mach_o::relocatable::Parser<x86_64>::hasObjC2Categories(fileContent); + } + else if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser<arm>::hasObjC2Categories(fileContent); + } + return false; +} + + + +} // namespace relocatable +} // namespace mach_o + + diff --git a/src/ld/parsers/macho_relocatable_file.h b/src/ld/parsers/macho_relocatable_file.h new file mode 100644 index 0000000..6d0e25e --- /dev/null +++ b/src/ld/parsers/macho_relocatable_file.h @@ -0,0 +1,58 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __MACHO_RELOCATABLE_FILE_H__ +#define __MACHO_RELOCATABLE_FILE_H__ + +#include "ld.hpp" +#include "Options.h" + +namespace mach_o { +namespace relocatable { + +struct ParserOptions { + uint32_t architecture; + bool objSubtypeMustMatch; + bool logAllFiles; + bool convertUnwindInfo; + uint32_t subType; +}; + +extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, + const char* path, time_t modTime, uint32_t ordinal, + const ParserOptions& opts); + +extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts); + +extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult); + +extern bool hasObjC2Categories(const uint8_t* fileContent); + +extern const char* archName(const uint8_t* fileContent); + +} // namespace relocatable +} // namespace mach_o + + +#endif // __MACHO_RELOCATABLE_FILE_H__ diff --git a/src/ld/parsers/opaque_section_file.cpp b/src/ld/parsers/opaque_section_file.cpp new file mode 100644 index 0000000..660f66d --- /dev/null +++ b/src/ld/parsers/opaque_section_file.cpp @@ -0,0 +1,106 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <vector> + +#include "ld.hpp" +#include "opaque_section_file.h" + + +namespace opaque_section { + + + +class Atom : public ld::Atom { +public: + virtual ld::File* file() const { return (ld::File*)&_file; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { memcpy(buffer, _content, _size); } + virtual void setScope(Scope) { } + +protected: + friend class File; + Atom(class File& f, const char* n, const uint8_t* content, uint64_t sz); + virtual ~Atom() {} + + class File& _file; + const char* _name; + const uint8_t* _content; + uint64_t _size; +}; + + +class File : public ld::File +{ +public: + File(const char* segmentName, const char* sectionName, const char* pth, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ord, + const char* symbolName="sect_create") + : ld::File(pth, 0, ord), + _atom(*this, symbolName, fileContent, fileLength), + _section(segmentName, sectionName, ld::Section::typeUnclassified) { } + virtual ~File() { } + + virtual bool forEachAtom(ld::File::AtomHandler& h) const { h.doAtom(_atom); return true; } + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const { return false; } + + ld::Atom* atom() { return &_atom; } +private: + friend class Atom; + + Atom _atom; + ld::Section _section; +}; + +Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _file(f), _name(n), _content(content), _size(sz) {} + + +// +// main function used by linker for -sectcreate +// +ld::File* parse(const char* segmentName, const char* sectionName, const char* path, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, + const char* symbolName) +{ + return new File(segmentName, sectionName, path, fileContent, fileLength, ordinal, symbolName); +} + + +} // namespace opaque_section + + + + + + diff --git a/src/ld/parsers/opaque_section_file.h b/src/ld/parsers/opaque_section_file.h new file mode 100644 index 0000000..04db805 --- /dev/null +++ b/src/ld/parsers/opaque_section_file.h @@ -0,0 +1,46 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __SECTION_FILE_H__ +#define __SECTION_FILE_H__ + + + +#include "ld.hpp" + +namespace opaque_section { + +extern ld::File* parse(const char* segmentName, const char* sectionName, const char* path, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ordinal, + const char* symbolName="opaque_section"); + + +} // namespace opaque_section + + + +#endif // __SECTION_FILE_H__ + + + diff --git a/src/ld/passes/branch_island.cpp b/src/ld/passes/branch_island.cpp new file mode 100644 index 0000000..d42fa54 --- /dev/null +++ b/src/ld/passes/branch_island.cpp @@ -0,0 +1,623 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> +#include <libkern/OSByteOrder.h> + +#include <vector> +#include <map> + +#include "MachOFileAbstraction.hpp" +#include "ld.hpp" +#include "branch_island.h" + +namespace ld { +namespace passes { +namespace branch_island { + + + + +struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; }; +class TargetAndOffsetComparor +{ +public: + bool operator()(const TargetAndOffset& left, const TargetAndOffset& right) const + { + if ( left.atom != right.atom ) + return ( left.atom < right.atom ); + return ( left.offset < right.offset ); + } +}; + + +static bool _s_log = false; +static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); + +class PPCBranchIslandAtom : public ld::Atom { +public: + PPCBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + int64_t displacement = _target->finalAddress() - this->finalAddress(); + const int64_t bl_sixteenMegLimit = 0x00FFFFFF; + if ( _target->contentType() == ld::Atom::typeBranchIsland ) { + // try optimizing away intermediate islands + int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress(); + if ( (skipToFinalDisplacement > bl_sixteenMegLimit) && (skipToFinalDisplacement < (-bl_sixteenMegLimit)) ) { + displacement = skipToFinalDisplacement; + } + } + int32_t branchInstruction = 0x48000000 | ((uint32_t)displacement & 0x03FFFFFC); + OSWriteBigInt32(buffer, 0, branchInstruction); + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + +class ARMtoARMBranchIslandAtom : public ld::Atom { +public: + ARMtoARMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + int64_t displacement = _target->finalAddress() - this->finalAddress() - 8; + if ( _target->contentType() == ld::Atom::typeBranchIsland ) { + // an ARM branch can branch farther than a thumb branch. The branch + // island generation was conservative and put islands every thumb + // branch distance apart. Check to see if this is a an island + // hopping branch that could be optimized to go directly to target. + int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 8; + if ( (skipToFinalDisplacement < 33554428LL) && (skipToFinalDisplacement > (-33554432LL)) ) { + // can skip branch island and jump straight to target + if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); + displacement = skipToFinalDisplacement; + } + else { + // ultimate target is too far, jump to island + if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + } + } + uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF; + int32_t branchInstruction = 0xEA000000 | imm24; + OSWriteLittleInt32(buffer, 0, branchInstruction); + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + + +class ARMtoThumb1BranchIslandAtom : public ld::Atom { +public: + ARMtoThumb1BranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // There is no large displacement thumb1 branch instruction. + // Instead use ARM instructions that can jump to thumb. + // we use a 32-bit displacement, so we can directly jump to target which means no island hopping + int64_t displacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - (this->finalAddress() + 12); + if ( _finalTarget.atom->isThumb() ) + displacement |= 1; + if (_s_log) fprintf(stderr, "%s: 4 ARM instruction jump to final target at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[12], 0, displacement); // .long target-this + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + + +class Thumb2toThumbBranchIslandAtom : public ld::Atom { +public: + Thumb2toThumbBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + int64_t displacement = _target->finalAddress() - this->finalAddress() - 4; + if ( _target->contentType() == ld::Atom::typeBranchIsland ) { + // an ARM branch can branch farther than a thumb branch. The branch + // island generation was conservative and put islands every thumb + // branch distance apart. Check to see if this is a an island + // hopping branch that could be optimized to go directly to target. + int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 4; + if ( (skipToFinalDisplacement < 16777214) && (skipToFinalDisplacement > (-16777216LL)) ) { + // can skip branch island and jump straight to target + if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); + displacement = skipToFinalDisplacement; + } + else { + // ultimate target is too far for thumb2 branch, jump to island + if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + } + } + // The instruction is really two instructions: + // The lower 16 bits are the first instruction, which contains the high + // 11 bits of the displacement. + // The upper 16 bits are the second instruction, which contains the low + // 11 bits of the displacement, as well as differentiating bl and blx. + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + uint32_t opcode = 0x9000F000; + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp; + //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", + // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); + OSWriteLittleInt32(buffer, 0, newInstruction); + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + +class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { +public: + NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(nm), + _target(target), + _finalTarget(finalTarget) { } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // There is no large displacement thumb1 branch instruction. + // Instead use ARM instructions that can jump to thumb. + // we use a 32-bit displacement, so we can directly jump to final target which means no island hopping + uint32_t targetAddr = _finalTarget.atom->finalAddress(); + if ( _finalTarget.atom->isThumb() ) + targetAddr |= 1; + if (_s_log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", + _target->name(), _finalTarget.atom->finalAddress()); + OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] + OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this + } + virtual void setScope(Scope) { } + +private: + const char* _name; + const ld::Atom* _target; + TargetAndOffset _finalTarget; +}; + + +static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, TargetAndOffset finalTarget) +{ + char* name; + if ( finalTarget.offset == 0 ) { + if ( islandRegion == 0 ) + asprintf(&name, "%s.island", finalTarget.atom->name()); + else + asprintf(&name, "%s.island.%d", finalTarget.atom->name(), islandRegion+1); + } + else { + asprintf(&name, "%s_plus_%d.island.%d", finalTarget.atom->name(), finalTarget.offset, islandRegion); + } + + switch ( kind ) { + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + return new PPCBranchIslandAtom(name, nextTarget, finalTarget); + break; + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + if ( finalTarget.atom->isThumb() ) { + if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) { + return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget); + } + else if ( opts.outputSlidable() ) { + return new ARMtoThumb1BranchIslandAtom(name, nextTarget, finalTarget); + } + else { + return new NoPicARMtoThumbMBranchIslandAtom(name, nextTarget, finalTarget); + } + } + else { + return new ARMtoARMBranchIslandAtom(name, nextTarget, finalTarget); + } + break; + default: + assert(0 && "unexpected branch kind"); + break; + } + return NULL; +} + + +static uint64_t textSizeWhenMightNeedBranchIslands(const Options& opts, bool seenThumbBranch) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + return 16000000; + break; + case CPU_TYPE_ARM: + if ( ! seenThumbBranch ) + return 32000000; // ARM can branch +/- 32MB + else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + return 16000000; // thumb2 can branch +/- 16MB + else + return 4000000; // thumb1 can branch +/- 4MB + break; + } + assert(0 && "unexpected architecture"); + return 0x100000000LL; +} + + +static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBranch) +{ + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + return 14*1024*1024; + break; + case CPU_TYPE_ARM: + if ( ! seenThumbBranch ) + return 30*1024*1024; // 2MB of branch islands per 32MB + else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + return 14*1024*1024; // 2MB of branch islands per 16MB + else + return 3500000; // 0.5MB of branch islands per 4MB + break; + } + assert(0 && "unexpected architecture"); + return 0x100000000LL; +} + + +// +// PowerPC can do PC relative branches as far as +/-16MB. +// If a branch target is >16MB then we insert one or more +// "branch islands" between the branch and its target that +// allows island hopping to the target. +// +// Branch Island Algorithm +// +// If the __TEXT segment < 16MB, then no branch islands needed +// Otherwise, every 14MB into the __TEXT segment a region is +// added which can contain branch islands. Every out-of-range +// bl instruction is checked. If it crosses a region, an island +// is added to that region with the same target and the bl is +// adjusted to target the island instead. +// +// In theory, if too many islands are added to one region, it +// could grow the __TEXT enough that other previously in-range +// bl branches could be pushed out of range. We reduce the +// probability this could happen by placing the ranges every +// 14MB which means the region would have to be 2MB (512,000 islands) +// before any branches could be pushed out of range. +// + +void doPass(const Options& opts, ld::Internal& state) +{ + // only make branch islands in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // only PowerPC and ARM need branch islands + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + case CPU_TYPE_ARM: + break; + default: + return; + } + + // scan to find __text section + ld::Internal::FinalSection* textSection = NULL; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->sectionName(), "__text") == 0 ) + textSection = sect; + } + if ( textSection == NULL ) + return; + + // assign section offsets to each atom in __text section, watch for thumb branches, and find total size + const bool isARM = (opts.architecture() == CPU_TYPE_ARM); + bool hasThumbBranches = false; + uint64_t offset = 0; + for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + // check for thumb branches + if ( isARM && ~hasThumbBranches ) { + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + hasThumbBranches = true; + break; + default: + break; + } + } + } + // align atom + ld::Atom::Alignment atomAlign = atom->alignment(); + uint64_t atomAlignP2 = (1 << atomAlign.powerOf2); + uint64_t currentModulus = (offset % atomAlignP2); + if ( currentModulus != atomAlign.modulus ) { + if ( atomAlign.modulus > currentModulus ) + offset += atomAlign.modulus-currentModulus; + else + offset += atomAlign.modulus+atomAlignP2-currentModulus; + } + (const_cast<ld::Atom*>(atom))->setSectionOffset(offset); + offset += atom->size(); + } + uint64_t totalTextSize = offset; + if ( totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches) ) + return; + if (_s_log) fprintf(stderr, "ld: __text section size=%llu, might need branch islands\n", totalTextSize); + + // figure out how many regions of branch islands will be needed + const uint32_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section + const int kIslandRegionsCount = totalTextSize / kBetweenRegions; + typedef std::map<TargetAndOffset,const ld::Atom*, TargetAndOffsetComparor> AtomToIsland; + AtomToIsland* regionsMap[kIslandRegionsCount]; + std::vector<const ld::Atom*>* regionsIslands[kIslandRegionsCount]; + for(int i=0; i < kIslandRegionsCount; ++i) { + regionsMap[i] = new AtomToIsland(); + regionsIslands[i] = new std::vector<const ld::Atom*>(); + } + unsigned int islandCount = 0; + if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); + + // create islands for branches in __text that are out of range + for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::Atom* target = NULL; + uint64_t addend = 0; + ld::Fixup* fixupWithTarget = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + fixupWithTarget = NULL; + addend = 0; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + fixupWithTarget = fit; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + fixupWithTarget = fit; + break; + } + bool haveBranch = false; + switch (fit->kind) { + case ld::Fixup::kindAddAddend: + addend = fit->u.addend; + break; + case ld::Fixup::kindStorePPCBranch24: + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + haveBranch = true; + break; + default: + break; + } + if ( haveBranch ) { + int64_t srcAddr = atom->sectionOffset() + fit->offsetInAtom; + int64_t dstAddr = target->sectionOffset() + addend; + if ( target->section().type() == ld::Section::typeStub ) + dstAddr = totalTextSize; + int64_t displacement = dstAddr - srcAddr; + TargetAndOffset finalTargetAndOffset = { target, addend }; + const int64_t kBranchLimit = kBetweenRegions; + if ( displacement > kBranchLimit ) { + // create forward branch chain + const ld::Atom* nextTarget = target; + for (int i=kIslandRegionsCount-1; i >=0 ; --i) { + AtomToIsland* region = regionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset); + (*region)[finalTargetAndOffset] = island; + if (_s_log) fprintf(stderr, "added island %s to region %d for %s\n", island->name(), i, atom->name()); + regionsIslands[i]->push_back(island); + ++islandCount; + nextTarget = island; + } + else { + nextTarget = pos->second; + } + } + } + if (_s_log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->name(), target->name(), atom->name()); + fixupWithTarget->u.target = nextTarget; + fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + } + else if ( displacement < (-kBranchLimit) ) { + // create back branching chain + const ld::Atom* prevTarget = target; + for (int i=0; i < kIslandRegionsCount ; ++i) { + AtomToIsland* region = regionsMap[i]; + int64_t islandRegionAddr = kBetweenRegions * (i+1); + if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset); + (*region)[finalTargetAndOffset] = island; + if (_s_log) fprintf(stderr, "added back island %s to region %d for %s\n", island->name(), i, atom->name()); + regionsIslands[i]->push_back(island); + ++islandCount; + prevTarget = island; + } + else { + prevTarget = pos->second; + } + } + } + if (_s_log) fprintf(stderr, "using back island %s for %s\n", prevTarget->name(), atom->name()); + fixupWithTarget->u.target = prevTarget; + fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + } + } + } + } + + + // insert islands into __text section and adjust section offsets + if ( islandCount > 0 ) { + if ( _s_log ) fprintf(stderr, "ld: %u branch islands required in %u regions\n", islandCount, kIslandRegionsCount); + std::vector<const ld::Atom*> newAtomList; + newAtomList.reserve(textSection->atoms.size()+islandCount); + uint64_t islandRegionAddr = kBetweenRegions;; + int regionIndex = 0; + for (std::vector<const ld::Atom*>::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { + const ld::Atom* atom = *it; + if ( (atom->sectionOffset()+atom->size()) > islandRegionAddr ) { + std::vector<const ld::Atom*>* regionIslands = regionsIslands[regionIndex]; + for (std::vector<const ld::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + const ld::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); + } + ++regionIndex; + islandRegionAddr += kBetweenRegions; + } + newAtomList.push_back(atom); + } + // put any remaining islands at end of __text section + if ( regionIndex < kIslandRegionsCount ) { + std::vector<const ld::Atom*>* regionIslands = regionsIslands[regionIndex]; + for (std::vector<const ld::Atom*>::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { + const ld::Atom* islandAtom = *rit; + newAtomList.push_back(islandAtom); + if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); + } + } + // swap in new list of atoms for __text section + textSection->atoms.clear(); + textSection->atoms = newAtomList; + } + +} + + +} // namespace branch_island +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/branch_island.h b/src/ld/passes/branch_island.h new file mode 100644 index 0000000..5daafe4 --- /dev/null +++ b/src/ld/passes/branch_island.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __BRANCH_ISLAND_H__ +#define __BRANCH_ISLAND_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace branch_island { + +// called by linker to branch islands if there are out of range branches +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace branch_island +} // namespace passes +} // namespace ld + +#endif // __BRANCH_ISLAND_H__ diff --git a/src/ld/passes/branch_shim.cpp b/src/ld/passes/branch_shim.cpp new file mode 100644 index 0000000..70ebcfc --- /dev/null +++ b/src/ld/passes/branch_shim.cpp @@ -0,0 +1,314 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <string.h> +#include <unistd.h> + +#include <vector> +#include <map> + +#include "MachOFileAbstraction.hpp" +#include "ld.hpp" +#include "branch_shim.h" + +namespace ld { +namespace passes { +namespace branch_shim { + + + +static bool _s_log = false; +static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode); + + + +class Thumb2ToArmShimAtom : public ld::Atom { +public: + Thumb2ToArmShimAtom(const ld::Atom* target) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(NULL), + _target(target), + _fixup1(8, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), + _fixup2(8, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(8, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup4(8, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { asprintf((char**)&_name, "%s$shim", target->name()); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // Use ARM instructions that can jump to thumb. + assert( ! _target->isThumb() ); + if (_s_log) fprintf(stderr, "3 Thumb2 instruction shim to jump to %s\n", _target->name()); + OSWriteLittleInt32(&buffer[0], 0, 0xc004f8df); // ldr ip, pc + 4 + OSWriteLittleInt16(&buffer[4], 0, 0x44fc); // add ip, pc, ip + OSWriteLittleInt16(&buffer[6], 0, 0x4760); // bx ip + OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // .long target-this + } + + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; +}; + + + +class Thumb1ToArmShimAtom : public ld::Atom { +public: + Thumb1ToArmShimAtom(const ld::Atom* target) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(NULL), + _target(target), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { asprintf((char**)&_name, "%s$shim", target->name()); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // Use ARM instructions that can jump to thumb. + assert( ! _target->isThumb() ); + if (_s_log) fprintf(stderr, "6 Thumb1 instruction shim to jump to %s\n", _target->name()); + OSWriteLittleInt16(&buffer[ 0], 0, 0xb402); // push {r1} + OSWriteLittleInt16(&buffer[ 2], 0, 0x4902); // ldr r1, [pc, #8] + OSWriteLittleInt16(&buffer[ 4], 0, 0x4479); // add r1, pc + OSWriteLittleInt16(&buffer[ 6], 0, 0x468c); // mov ip, r1 + OSWriteLittleInt16(&buffer[ 8], 0, 0xbc02); // pop {r1} + OSWriteLittleInt16(&buffer[10], 0, 0x4760); // bx ip + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long target-this + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; +}; + + + + +class ARMtoThumbShimAtom : public ld::Atom { +public: + ARMtoThumbShimAtom(const ld::Atom* target) + : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _name(NULL), + _target(target), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { asprintf((char**)&_name, "%s$shim", target->name()); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // Use ARM instructions that can jump to thumb. + assert( _target->isThumb() ); + if (_s_log) fprintf(stderr, "4 ARM instruction shim to jump to %s\n", _target->name()); + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 4 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[12], 0, 0); // .long target-this + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const char* _name; + const ld::Atom* _target; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; +}; + + + + + + +static void extractTarget(ld::Fixup::iterator fixup, ld::Internal& state, const ld::Atom** target) +{ + switch ( fixup->binding ) { + case ld::Fixup::bindingNone: + throw "unexpected bindingNone"; + case ld::Fixup::bindingByNameUnbound: + throw "unexpected bindingByNameUnbound"; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + *target = fixup->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + *target = state.indirectBindingTable[fixup->u.bindingIndex]; + break; + } +} + + + +// +// The tail-call optimzation may result in a function ending in a jump (b) +// to another functions. At compile time the compiler does not know +// if the target of the jump will be in the same mode (arm vs thumb). +// The arm/thumb instruction set has a way to change modes in a bl(x) +// insruction, but no instruction to change mode in a jump (b) instruction. +// In those rare cases, the linker needs to insert a shim of code to +// make the mode switch. +// +void doPass(const Options& opts, ld::Internal& state) +{ + std::map<const Atom*, const Atom*> atomToThumbMap; + std::map<const Atom*, const Atom*> thumbToAtomMap; + std::vector<const Atom*> shims; + + // only make branch shims in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // only ARM need branch islands + if ( opts.architecture() != CPU_TYPE_ARM ) + return; + + // scan to find __text section + ld::Internal::FinalSection* textSection = NULL; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( strcmp(sect->sectionName(), "__text") == 0 ) + textSection = sect; + } + if ( textSection == NULL ) + return; + + // scan __text section for branch instructions that need to switch mode + for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::Atom* target; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + extractTarget(fit, state, &target); + if ( ! target->isThumb() ) { + const uint8_t* fixUpLocation = atom->rawContentPointer() + fit->offsetInAtom; + uint32_t instruction = *((uint32_t*)fixUpLocation); + bool is_b = ((instruction & 0xD000F800) == 0x9000F000); + if ( is_b ) { + fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); + const Atom* shim = NULL; + std::map<const Atom*, const Atom*>::iterator pos = thumbToAtomMap.find(target); + if ( pos == thumbToAtomMap.end() ) { + if ( opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) + shim = new Thumb2ToArmShimAtom(target); + else + shim = new Thumb1ToArmShimAtom(target); + shims.push_back(shim); + thumbToAtomMap[target] = shim; + } + else { + shim = pos->second; + } + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = shim; + } + } + break; + case ld::Fixup::kindStoreTargetAddressARMBranch24: + extractTarget(fit, state, &target); + if ( target->isThumb() ) { + const uint8_t* fixUpLocation = atom->rawContentPointer() + fit->offsetInAtom; + uint32_t instruction = *((uint32_t*)fixUpLocation); + bool is_b = ((instruction & 0x0F000000) == 0x0A000000); + if ( is_b ) { + fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); + const Atom* shim = NULL; + std::map<const Atom*, const Atom*>::iterator pos = atomToThumbMap.find(target); + if ( pos == atomToThumbMap.end() ) { + shim = new ARMtoThumbShimAtom(target); + shims.push_back(shim); + atomToThumbMap[target] = shim; + } + else { + shim = pos->second; + } + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = shim; + } + } + break; + + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreThumbBranch22: + fprintf(stderr, "found branch-22 without store in %s\n", atom->name()); + break; + default: + break; + } + } + } + + // append all new shims to end of __text + textSection->atoms.insert(textSection->atoms.end(), shims.begin(), shims.end()); +} + + +} // namespace branch_shim +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/branch_shim.h b/src/ld/passes/branch_shim.h new file mode 100644 index 0000000..ab6b959 --- /dev/null +++ b/src/ld/passes/branch_shim.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __BRANCH_SHIM_H__ +#define __BRANCH_SHIM_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace branch_shim { + +// called by linker to branch shims to support branch (but not bl) that switches mode +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace branch_shim +} // namespace passes +} // namespace ld + +#endif // __BRANCH_SHIM_H__ diff --git a/src/ld/passes/compact_unwind.cpp b/src/ld/passes/compact_unwind.cpp new file mode 100644 index 0000000..5b9434e --- /dev/null +++ b/src/ld/passes/compact_unwind.cpp @@ -0,0 +1,794 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> +#include <mach/machine.h> +#include <mach-o/compact_unwind_encoding.h> + +#include <vector> +#include <map> + +#include "ld.hpp" +#include "compact_unwind.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + + +namespace ld { +namespace passes { +namespace compact_unwind { + + +struct UnwindEntry { + UnwindEntry(const ld::Atom* f, uint64_t a, uint32_t o, const ld::Atom* d, + const ld::Atom* l, const ld::Atom* p, uint32_t en) + : func(f), fde(d), lsda(l), personalityPointer(p), funcTentAddress(a), + functionOffset(o), encoding(en) { } + const ld::Atom* func; + const ld::Atom* fde; + const ld::Atom* lsda; + const ld::Atom* personalityPointer; + uint64_t funcTentAddress; + uint32_t functionOffset; + compact_unwind_encoding_t encoding; +}; + +struct LSDAEntry { + const ld::Atom* func; + const ld::Atom* lsda; +}; + + +template <typename A> +class UnwindInfoAtom : public ld::Atom { +public: + UnwindInfoAtom(const std::vector<UnwindEntry>& entries,uint64_t ehFrameSize); + ~UnwindInfoAtom(); + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "compact unwind info"; } + virtual uint64_t size() const { return _headerSize+_pagesSize; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const; + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; } + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + typedef macho_unwind_info_compressed_second_level_page_header<P> CSLP; + + bool encodingMeansUseDwarf(compact_unwind_encoding_t enc); + void compressDuplicates(const std::vector<UnwindEntry>& entries, + std::vector<UnwindEntry>& uniqueEntries); + void makePersonalityIndexes(std::vector<UnwindEntry>& entries, + std::map<const ld::Atom*, uint32_t>& personalityIndexMap); + void findCommonEncoding(const std::vector<UnwindEntry>& entries, + std::map<compact_unwind_encoding_t, unsigned int>& commonEncodings); + void makeLsdaIndex(const std::vector<UnwindEntry>& entries, std::vector<LSDAEntry>& lsdaIndex, + std::map<const ld::Atom*, uint32_t>& lsdaIndexOffsetMap); + unsigned int makeCompressedSecondLevelPage(const std::vector<UnwindEntry>& uniqueInfos, + const std::map<compact_unwind_encoding_t,unsigned int> commonEncodings, + uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd); + unsigned int makeRegularSecondLevelPage(const std::vector<UnwindEntry>& uniqueInfos, uint32_t pageSize, + unsigned int endIndex, uint8_t*& pageEnd); + void addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc); + void addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde); + void addRegularAddressFixup(uint32_t offset, const ld::Atom* func); + void addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde); + void addImageOffsetFixup(uint32_t offset, const ld::Atom* targ); + void addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend); + + uint8_t* _pagesForDelete; + uint8_t* _pages; + uint64_t _pagesSize; + uint8_t* _header; + uint64_t _headerSize; + std::vector<ld::Fixup> _fixups; + + static bool _s_log; + static ld::Section _s_section; +}; + +template <typename A> +bool UnwindInfoAtom<A>::_s_log = false; + +template <typename A> +ld::Section UnwindInfoAtom<A>::_s_section("__TEXT", "__unwind_info", ld::Section::typeUnwindInfo); + + +template <typename A> +UnwindInfoAtom<A>::UnwindInfoAtom(const std::vector<UnwindEntry>& entries, uint64_t ehFrameSize) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _pagesForDelete(NULL), _pages(NULL), _pagesSize(0), _header(NULL), _headerSize(0) +{ + // build new compressed list by removing entries where next function has same encoding + std::vector<UnwindEntry> uniqueEntries; + compressDuplicates(entries, uniqueEntries); + + // reserve room so _fixups vector is not reallocated a bunch of times + _fixups.reserve(uniqueEntries.size()*3); + + // build personality index, update encodings with personality index + std::map<const ld::Atom*, uint32_t> personalityIndexMap; + makePersonalityIndexes(uniqueEntries, personalityIndexMap); + if ( personalityIndexMap.size() > 3 ) { + warning("too many personality routines for compact unwind to encode"); + return; + } + + // put the most common encodings into the common table, but at most 127 of them + std::map<compact_unwind_encoding_t, unsigned int> commonEncodings; + findCommonEncoding(uniqueEntries, commonEncodings); + + // build lsda index + std::map<const ld::Atom*, uint32_t> lsdaIndexOffsetMap; + std::vector<LSDAEntry> lsdaIndex; + makeLsdaIndex(uniqueEntries, lsdaIndex, lsdaIndexOffsetMap); + + + // calculate worst case size for all unwind info pages when allocating buffer + const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + assert(uniqueEntries.size() > 0); + const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 1; + _pagesForDelete = (uint8_t*)calloc(pageCount,4096); + if ( _pagesForDelete == NULL ) { + warning("could not allocate space for compact unwind info"); + return; + } + + // make last second level page smaller so that all other second level pages can be page aligned + uint32_t maxLastPageSize = 4096 - (ehFrameSize % 4096); + uint32_t tailPad = 0; + if ( maxLastPageSize < 128 ) { + tailPad = maxLastPageSize; + maxLastPageSize = 4096; + } + + // fill in pages in reverse order + const ld::Atom* secondLevelFirstFuncs[pageCount*3]; + uint8_t* secondLevelPagesStarts[pageCount*3]; + unsigned int endIndex = uniqueEntries.size(); + unsigned int secondLevelPageCount = 0; + uint8_t* pageEnd = &_pagesForDelete[pageCount*4096]; + uint32_t pageSize = maxLastPageSize; + while ( endIndex > 0 ) { + endIndex = makeCompressedSecondLevelPage(uniqueEntries, commonEncodings, pageSize, endIndex, pageEnd); + secondLevelPagesStarts[secondLevelPageCount] = pageEnd; + secondLevelFirstFuncs[secondLevelPageCount] = uniqueEntries[endIndex].func; + ++secondLevelPageCount; + pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size + } + _pages = pageEnd; + _pagesSize = &_pagesForDelete[pageCount*4096] - pageEnd; + + + // calculate section layout + const uint32_t commonEncodingsArraySectionOffset = sizeof(macho_unwind_info_section_header<P>); + const uint32_t commonEncodingsArrayCount = commonEncodings.size(); + const uint32_t commonEncodingsArraySize = commonEncodingsArrayCount * sizeof(compact_unwind_encoding_t); + const uint32_t personalityArraySectionOffset = commonEncodingsArraySectionOffset + commonEncodingsArraySize; + const uint32_t personalityArrayCount = personalityIndexMap.size(); + const uint32_t personalityArraySize = personalityArrayCount * sizeof(uint32_t); + const uint32_t indexSectionOffset = personalityArraySectionOffset + personalityArraySize; + const uint32_t indexCount = secondLevelPageCount+1; + const uint32_t indexSize = indexCount * sizeof(macho_unwind_info_section_header_index_entry<P>); + const uint32_t lsdaIndexArraySectionOffset = indexSectionOffset + indexSize; + const uint32_t lsdaIndexArrayCount = lsdaIndex.size(); + const uint32_t lsdaIndexArraySize = lsdaIndexArrayCount * sizeof(macho_unwind_info_section_header_lsda_index_entry<P>); + const uint32_t headerEndSectionOffset = lsdaIndexArraySectionOffset + lsdaIndexArraySize; + + // now that we know the size of the header, slide all existing fixups on the pages + const int32_t fixupSlide = headerEndSectionOffset + (_pagesForDelete - _pages); + for(std::vector<ld::Fixup>::iterator it = _fixups.begin(); it != _fixups.end(); ++it) { + it->offsetInAtom += fixupSlide; + } + + // allocate and fill in section header + _headerSize = headerEndSectionOffset; + _header = new uint8_t[_headerSize]; + bzero(_header, _headerSize); + macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)_header; + sectionHeader->set_version(UNWIND_SECTION_VERSION); + sectionHeader->set_commonEncodingsArraySectionOffset(commonEncodingsArraySectionOffset); + sectionHeader->set_commonEncodingsArrayCount(commonEncodingsArrayCount); + sectionHeader->set_personalityArraySectionOffset(personalityArraySectionOffset); + sectionHeader->set_personalityArrayCount(personalityArrayCount); + sectionHeader->set_indexSectionOffset(indexSectionOffset); + sectionHeader->set_indexCount(indexCount); + + // copy common encodings + uint32_t* commonEncodingsTable = (uint32_t*)&_header[commonEncodingsArraySectionOffset]; + for (std::map<uint32_t, unsigned int>::iterator it=commonEncodings.begin(); it != commonEncodings.end(); ++it) + E::set32(commonEncodingsTable[it->second], it->first); + + // make references for personality entries + uint32_t* personalityArray = (uint32_t*)&_header[sectionHeader->personalityArraySectionOffset()]; + for (std::map<const ld::Atom*, unsigned int>::iterator it=personalityIndexMap.begin(); it != personalityIndexMap.end(); ++it) { + uint32_t offset = (uint8_t*)&personalityArray[it->second-1] - _header; + this->addImageOffsetFixup(offset, it->first); + } + + // build first level index and references + macho_unwind_info_section_header_index_entry<P>* indexTable = (macho_unwind_info_section_header_index_entry<P>*)&_header[indexSectionOffset]; + uint32_t refOffset; + for (unsigned int i=0; i < secondLevelPageCount; ++i) { + unsigned int reverseIndex = secondLevelPageCount - 1 - i; + indexTable[i].set_functionOffset(0); + indexTable[i].set_secondLevelPagesSectionOffset(secondLevelPagesStarts[reverseIndex]-_pages+headerEndSectionOffset); + indexTable[i].set_lsdaIndexArraySectionOffset(lsdaIndexOffsetMap[secondLevelFirstFuncs[reverseIndex]]+lsdaIndexArraySectionOffset); + refOffset = (uint8_t*)&indexTable[i] - _header; + this->addImageOffsetFixup(refOffset, secondLevelFirstFuncs[reverseIndex]); + } + indexTable[secondLevelPageCount].set_functionOffset(0); + indexTable[secondLevelPageCount].set_secondLevelPagesSectionOffset(0); + indexTable[secondLevelPageCount].set_lsdaIndexArraySectionOffset(lsdaIndexArraySectionOffset+lsdaIndexArraySize); + refOffset = (uint8_t*)&indexTable[secondLevelPageCount] - _header; + this->addImageOffsetFixupPlusAddend(refOffset, entries.back().func, entries.back().func->size()+1); + + // build lsda references + uint32_t lsdaEntrySectionOffset = lsdaIndexArraySectionOffset; + for (std::vector<LSDAEntry>::iterator it = lsdaIndex.begin(); it != lsdaIndex.end(); ++it) { + this->addImageOffsetFixup(lsdaEntrySectionOffset, it->func); + this->addImageOffsetFixup(lsdaEntrySectionOffset+4, it->lsda); + lsdaEntrySectionOffset += sizeof(unwind_info_section_header_lsda_index_entry); + } + +} + +template <typename A> +UnwindInfoAtom<A>::~UnwindInfoAtom() +{ + free(_pagesForDelete); + free(_header); +} + +template <typename A> +void UnwindInfoAtom<A>::copyRawContent(uint8_t buffer[]) const +{ + // content is in two parts + memcpy(buffer, _header, _headerSize); + memcpy(&buffer[_headerSize], _pages, _pagesSize); +} + + +template <> +bool UnwindInfoAtom<x86>::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); +} + +template <> +bool UnwindInfoAtom<x86_64>::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); +} + +template <typename A> +void UnwindInfoAtom<A>::compressDuplicates(const std::vector<UnwindEntry>& entries, std::vector<UnwindEntry>& uniqueEntries) +{ + // build new list removing entries where next function has same encoding + uniqueEntries.reserve(entries.size()); + UnwindEntry last(NULL, 0, 0, NULL, NULL, NULL, 0xFFFFFFFF); + for(std::vector<UnwindEntry>::const_iterator it=entries.begin(); it != entries.end(); ++it) { + const UnwindEntry& next = *it; + bool newNeedsDwarf = encodingMeansUseDwarf(next.encoding); + // remove entries which have same encoding and personalityPointer as last one + if ( newNeedsDwarf || (next.encoding != last.encoding) || (next.personalityPointer != last.personalityPointer) + || (next.lsda != NULL) || (last.lsda != NULL) ) { + uniqueEntries.push_back(next); + } + last = next; + } + if (_s_log) fprintf(stderr, "compressDuplicates() entries.size()=%lu, uniqueEntries.size()=%lu\n", + entries.size(), uniqueEntries.size()); +} + +template <typename A> +void UnwindInfoAtom<A>::makePersonalityIndexes(std::vector<UnwindEntry>& entries, std::map<const ld::Atom*, uint32_t>& personalityIndexMap) +{ + for(std::vector<UnwindEntry>::iterator it=entries.begin(); it != entries.end(); ++it) { + if ( it->personalityPointer != NULL ) { + std::map<const ld::Atom*, uint32_t>::iterator pos = personalityIndexMap.find(it->personalityPointer); + if ( pos == personalityIndexMap.end() ) { + const uint32_t nextIndex = personalityIndexMap.size() + 1; + personalityIndexMap[it->personalityPointer] = nextIndex; + } + uint32_t personalityIndex = personalityIndexMap[it->personalityPointer]; + it->encoding |= (personalityIndex << (__builtin_ctz(UNWIND_PERSONALITY_MASK)) ); + } + } + if (_s_log) fprintf(stderr, "makePersonalityIndexes() %lu personality routines used\n", personalityIndexMap.size()); +} + + +template <typename A> +void UnwindInfoAtom<A>::findCommonEncoding(const std::vector<UnwindEntry>& entries, + std::map<compact_unwind_encoding_t, unsigned int>& commonEncodings) +{ + // scan infos to get frequency counts for each encoding + std::map<compact_unwind_encoding_t, unsigned int> encodingsUsed; + unsigned int mostCommonEncodingUsageCount = 0; + for(std::vector<UnwindEntry>::const_iterator it=entries.begin(); it != entries.end(); ++it) { + // never put dwarf into common table + if ( encodingMeansUseDwarf(it->encoding) ) + continue; + std::map<compact_unwind_encoding_t, unsigned int>::iterator pos = encodingsUsed.find(it->encoding); + if ( pos == encodingsUsed.end() ) { + encodingsUsed[it->encoding] = 1; + } + else { + encodingsUsed[it->encoding] += 1; + if ( mostCommonEncodingUsageCount < encodingsUsed[it->encoding] ) + mostCommonEncodingUsageCount = encodingsUsed[it->encoding]; + } + } + // put the most common encodings into the common table, but at most 127 of them + for(unsigned int usages=mostCommonEncodingUsageCount; usages > 1; --usages) { + for (std::map<compact_unwind_encoding_t, unsigned int>::iterator euit=encodingsUsed.begin(); euit != encodingsUsed.end(); ++euit) { + if ( euit->second == usages ) { + unsigned int sz = commonEncodings.size(); + if ( sz < 127 ) { + commonEncodings[euit->first] = sz; + } + } + } + } + if (_s_log) fprintf(stderr, "findCommonEncoding() %lu common encodings found\n", commonEncodings.size()); +} + + +template <typename A> +void UnwindInfoAtom<A>::makeLsdaIndex(const std::vector<UnwindEntry>& entries, std::vector<LSDAEntry>& lsdaIndex, std::map<const ld::Atom*, uint32_t>& lsdaIndexOffsetMap) +{ + for(std::vector<UnwindEntry>::const_iterator it=entries.begin(); it != entries.end(); ++it) { + lsdaIndexOffsetMap[it->func] = lsdaIndex.size() * sizeof(unwind_info_section_header_lsda_index_entry); + if ( it->lsda != NULL ) { + LSDAEntry entry; + entry.func = it->func; + entry.lsda = it->lsda; + lsdaIndex.push_back(entry); + } + } + if (_s_log) fprintf(stderr, "makeLsdaIndex() %lu LSDAs found\n", lsdaIndex.size()); +} + + +template <> +void UnwindInfoAtom<x86>::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom<x86_64>::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom<x86>::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom<x86_64>::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + + +template <> +void UnwindInfoAtom<x86>::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom<x86_64>::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom<x86>::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom<x86_64>::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> +void UnwindInfoAtom<x86>::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom<x86_64>::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom<x86>::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> +void UnwindInfoAtom<x86_64>::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} + + + + + +template <typename A> +unsigned int UnwindInfoAtom<A>::makeRegularSecondLevelPage(const std::vector<UnwindEntry>& uniqueInfos, uint32_t pageSize, + unsigned int endIndex, uint8_t*& pageEnd) +{ + const unsigned int maxEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + const unsigned int entriesToAdd = ((endIndex > maxEntriesPerPage) ? maxEntriesPerPage : endIndex); + uint8_t* pageStart = pageEnd + - entriesToAdd*sizeof(unwind_info_regular_second_level_entry) + - sizeof(unwind_info_regular_second_level_page_header); + macho_unwind_info_regular_second_level_page_header<P>* page = (macho_unwind_info_regular_second_level_page_header<P>*)pageStart; + page->set_kind(UNWIND_SECOND_LEVEL_REGULAR); + page->set_entryPageOffset(sizeof(macho_unwind_info_regular_second_level_page_header<P>)); + page->set_entryCount(entriesToAdd); + macho_unwind_info_regular_second_level_entry<P>* entryTable = (macho_unwind_info_regular_second_level_entry<P>*)(pageStart + page->entryPageOffset()); + for (unsigned int i=0; i < entriesToAdd; ++i) { + const UnwindEntry& info = uniqueInfos[endIndex-entriesToAdd+i]; + entryTable[i].set_functionOffset(0); + entryTable[i].set_encoding(info.encoding); + // add fixup for address part of entry + uint32_t offset = (uint8_t*)(&entryTable[i]) - _pagesForDelete; + this->addRegularAddressFixup(offset, info.func); + if ( encodingMeansUseDwarf(info.encoding) ) { + // add fixup for dwarf offset part of page specific encoding + uint32_t encOffset = (uint8_t*)(&entryTable[i]) - _pagesForDelete; + this->addRegularFDEOffsetFixup(encOffset, info.fde); + } + } + if (_s_log) fprintf(stderr, "regular page with %u entries\n", entriesToAdd); + pageEnd = pageStart; + return endIndex - entriesToAdd; +} + + +template <typename A> +unsigned int UnwindInfoAtom<A>::makeCompressedSecondLevelPage(const std::vector<UnwindEntry>& uniqueInfos, + const std::map<compact_unwind_encoding_t,unsigned int> commonEncodings, + uint32_t pageSize, unsigned int endIndex, uint8_t*& pageEnd) +{ + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(pageSize=%u, endIndex=%u)\n", pageSize, endIndex); + // first pass calculates how many compressed entries we could fit in this sized page + // keep adding entries to page until: + // 1) encoding table plus entry table plus header exceed page size + // 2) the file offset delta from the first to last function > 24 bits + // 3) custom encoding index reachs 255 + // 4) run out of uniqueInfos to encode + std::map<compact_unwind_encoding_t, unsigned int> pageSpecificEncodings; + uint32_t space4 = (pageSize - sizeof(unwind_info_compressed_second_level_page_header))/sizeof(uint32_t); + std::vector<uint8_t> encodingIndexes; + int index = endIndex-1; + int entryCount = 0; + uint64_t lastEntryAddress = uniqueInfos[index].funcTentAddress; + bool canDo = true; + while ( canDo && (index >= 0) ) { + const UnwindEntry& info = uniqueInfos[index--]; + // compute encoding index + unsigned int encodingIndex; + std::map<compact_unwind_encoding_t, unsigned int>::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) { + encodingIndex = pos->second; + } + else { + // no commmon entry, so add one on this page + uint32_t encoding = info.encoding; + if ( encodingMeansUseDwarf(encoding) ) { + // make unique pseudo encoding so this dwarf will gets is own encoding entry slot + encoding += (index+1); + } + std::map<compact_unwind_encoding_t, unsigned int>::iterator ppos = pageSpecificEncodings.find(encoding); + if ( ppos != pageSpecificEncodings.end() ) { + encodingIndex = pos->second; + } + else { + encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); + if ( encodingIndex <= 255 ) { + pageSpecificEncodings[encoding] = encodingIndex; + } + else { + canDo = false; // case 3) + if (_s_log) fprintf(stderr, "end of compressed page with %u entries, %lu custom encodings because too many custom encodings\n", + entryCount, pageSpecificEncodings.size()); + } + } + } + if ( canDo ) + encodingIndexes.push_back(encodingIndex); + // compute function offset + uint32_t funcOffsetWithInPage = lastEntryAddress - info.funcTentAddress; + if ( funcOffsetWithInPage > 0x00FFFF00 ) { + // don't use 0x00FFFFFF because addresses may vary after atoms are laid out again + canDo = false; // case 2) + if (_s_log) fprintf(stderr, "can't use compressed page with %u entries because function offset too big\n", entryCount); + } + else { + ++entryCount; + } + // check room for entry + if ( (pageSpecificEncodings.size()+entryCount) >= space4 ) { + canDo = false; // case 1) + --entryCount; + if (_s_log) fprintf(stderr, "end of compressed page with %u entries because full\n", entryCount); + } + //if (_s_log) fprintf(stderr, "space4=%d, pageSpecificEncodings.size()=%ld, entryCount=%d\n", space4, pageSpecificEncodings.size(), entryCount); + } + + // check for cases where it would be better to use a regular (non-compressed) page + const unsigned int compressPageUsed = sizeof(unwind_info_compressed_second_level_page_header) + + pageSpecificEncodings.size()*sizeof(uint32_t) + + entryCount*sizeof(uint32_t); + if ( (compressPageUsed < (pageSize-4) && (index >= 0) ) ) { + const int regularEntriesPerPage = (pageSize - sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); + if ( entryCount < regularEntriesPerPage ) { + return makeRegularSecondLevelPage(uniqueInfos, pageSize, endIndex, pageEnd); + } + } + + // check if we need any padding because adding another entry would take 8 bytes but only have room for 4 + uint32_t pad = 0; + if ( compressPageUsed == (pageSize-4) ) + pad = 4; + + // second pass fills in page + uint8_t* pageStart = pageEnd - compressPageUsed - pad; + CSLP* page = (CSLP*)pageStart; + page->set_kind(UNWIND_SECOND_LEVEL_COMPRESSED); + page->set_entryPageOffset(sizeof(CSLP)); + page->set_entryCount(entryCount); + page->set_encodingsPageOffset(page->entryPageOffset()+entryCount*sizeof(uint32_t)); + page->set_encodingsCount(pageSpecificEncodings.size()); + uint32_t* const encodingsArray = (uint32_t*)&pageStart[page->encodingsPageOffset()]; + // fill in entry table + uint32_t* const entiresArray = (uint32_t*)&pageStart[page->entryPageOffset()]; + const ld::Atom* firstFunc = uniqueInfos[endIndex-entryCount].func; + for(unsigned int i=endIndex-entryCount; i < endIndex; ++i) { + const UnwindEntry& info = uniqueInfos[i]; + uint8_t encodingIndex; + if ( encodingMeansUseDwarf(info.encoding) ) { + // dwarf entries are always in page specific encodings + encodingIndex = pageSpecificEncodings[info.encoding+i]; + } + else { + std::map<uint32_t, unsigned int>::const_iterator pos = commonEncodings.find(info.encoding); + if ( pos != commonEncodings.end() ) + encodingIndex = pos->second; + else + encodingIndex = pageSpecificEncodings[info.encoding]; + } + uint32_t entryIndex = i - endIndex + entryCount; + E::set32(entiresArray[entryIndex], encodingIndex << 24); + // add fixup for address part of entry + uint32_t offset = (uint8_t*)(&entiresArray[entryIndex]) - _pagesForDelete; + this->addCompressedAddressOffsetFixup(offset, info.func, firstFunc); + if ( encodingMeansUseDwarf(info.encoding) ) { + // add fixup for dwarf offset part of page specific encoding + uint32_t encOffset = (uint8_t*)(&encodingsArray[encodingIndex-commonEncodings.size()]) - _pagesForDelete; + this->addCompressedEncodingFixup(encOffset, info.fde); + } + } + // fill in encodings table + for(std::map<uint32_t, unsigned int>::const_iterator it = pageSpecificEncodings.begin(); it != pageSpecificEncodings.end(); ++it) { + E::set32(encodingsArray[it->second-commonEncodings.size()], it->first); + } + + if (_s_log) fprintf(stderr, "compressed page with %u entries, %lu custom encodings\n", entryCount, pageSpecificEncodings.size()); + + // update pageEnd; + pageEnd = pageStart; + return endIndex-entryCount; // endIndex for next page +} + + + + + + +static uint64_t calculateEHFrameSize(const ld::Internal& state) +{ + uint64_t size = 0; + for (std::vector<ld::Internal::FinalSection*>::const_iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeCFI ) { + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + size += (*ait)->size(); + } + } + } + return size; +} + +static void getAllUnwindInfos(const ld::Internal& state, std::vector<UnwindEntry>& entries) +{ + uint64_t address = 0; + for (std::vector<ld::Internal::FinalSection*>::const_iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + // adjust address for atom alignment + uint64_t alignment = 1 << atom->alignment().powerOf2; + uint64_t currentModulus = (address % alignment); + uint64_t requiredModulus = atom->alignment().modulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + address += requiredModulus-currentModulus; + else + address += requiredModulus+alignment-currentModulus; + } + + if ( atom->beginUnwind() == atom->endUnwind() ) { + // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info + if ( atom->section().type() == ld::Section::typeCode ) { + entries.push_back(UnwindEntry(atom, address, 0, NULL, NULL, NULL, 0)); + } + } + else { + // atom has unwind info(s), add entry for each + const ld::Atom* fde = NULL; + const ld::Atom* lsda = NULL; + const ld::Atom* personalityPointer = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindNoneGroupSubordinateFDE: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + fde = fit->u.target; + break; + case ld::Fixup::kindNoneGroupSubordinateLSDA: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + lsda = fit->u.target; + break; + default: + break; + } + } + if ( fde != NULL ) { + // find CIE for this FDE + const ld::Atom* cie = NULL; + for (ld::Fixup::iterator fit = fde->fixupsBegin(), end=fde->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind != ld::Fixup::kindSubtractTargetAddress ) + continue; + if ( fit->binding != ld::Fixup::bindingDirectlyBound ) + continue; + cie = fit->u.target; + // CIE is only direct subtracted target in FDE + assert(cie->section().type() == ld::Section::typeCFI); + break; + } + if ( cie != NULL ) { + // if CIE can have just one fixup - to the personality pointer + for (ld::Fixup::iterator fit = cie->fixupsBegin(), end=cie->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind == ld::Fixup::kindSetTargetAddress ) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + personalityPointer = state.indirectBindingTable[fit->u.bindingIndex]; + assert(personalityPointer->section().type() == ld::Section::typeNonLazyPointer); + break; + case ld::Fixup::bindingDirectlyBound: + personalityPointer = fit->u.target; + assert(personalityPointer->section().type() == ld::Section::typeNonLazyPointer); + break; + default: + break; + } + } + } + } + } + for ( ld::Atom::UnwindInfo::iterator uit = atom->beginUnwind(); uit != atom->endUnwind(); ++uit ) { + entries.push_back(UnwindEntry(atom, address, uit->startOffset, fde, lsda, personalityPointer, uit->unwindInfo)); + } + } + address += atom->size(); + } + } +} + + + + +void doPass(const Options& opts, ld::Internal& state) +{ + //const bool log = false; + + // only make make __unwind_info in final linked images + if ( !opts.needsUnwindInfoSection() ) + return; + + // walk every atom and gets its unwind info + std::vector<UnwindEntry> entries; + entries.reserve(64); + getAllUnwindInfos(state, entries); + + // don't generate an __unwind_info section if there is no code in this linkage unit + if ( entries.size() == 0 ) + return; + + // calculate size of __eh_frame section, so __unwind_info can go before it and page align + uint64_t ehFrameSize = calculateEHFrameSize(state); + + // create atom that contains the whole compact unwind table + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + state.addAtom(*new UnwindInfoAtom<x86_64>(entries, ehFrameSize)); + break; + case CPU_TYPE_I386: + state.addAtom(*new UnwindInfoAtom<x86>(entries, ehFrameSize)); + break; + default: + assert(0 && "no compact unwind for arch"); + } +} + + +} // namespace compact_unwind +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/compact_unwind.h b/src/ld/passes/compact_unwind.h new file mode 100644 index 0000000..c6dc9f5 --- /dev/null +++ b/src/ld/passes/compact_unwind.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __COMPACT_UNWIND_H__ +#define __COMPACT_UNWIND_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace compact_unwind { + +// called by linker to add __unwind_info section table +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace compact_unwind +} // namespace passes +} // namespace ld + +#endif // __COMPACT_UNWIND_H__ diff --git a/src/ld/passes/dtrace_dof.cpp b/src/ld/passes/dtrace_dof.cpp new file mode 100644 index 0000000..f847cc0 --- /dev/null +++ b/src/ld/passes/dtrace_dof.cpp @@ -0,0 +1,339 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> + +#include <vector> +#include <map> +#include <ext/hash_map> + +#include "ld.hpp" +#include "dtrace_dof.h" + +// prototype for entry point in libdtrace.dylib +typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size); + + +namespace ld { +namespace passes { +namespace dtrace { + +class File; // forward reference + +class Atom : public ld::Atom { +public: + Atom(class File& f, const char* n, const uint8_t* content, uint64_t sz); + + virtual ld::File* file() const { return (ld::File*)&_file; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { memcpy(buffer, _content, _size); } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return &_fixups[_fixups.size()]; } + +protected: + friend class File; + virtual ~Atom() {} + + class File& _file; + const char* _name; + const uint8_t* _content; + uint64_t _size; + mutable std::vector<ld::Fixup> _fixups; +}; + + +class File : public ld::File +{ +public: + File(const char* segmentName, const char* sectionName, const char* pth, + const uint8_t fileContent[], uint64_t fileLength, uint32_t ord, + const char* symbolName="dof") + : ld::File(pth, 0, ord), + _atom(*this, symbolName, fileContent, fileLength), + _section(segmentName, sectionName, ld::Section::typeDtraceDOF) { } + virtual ~File() {} + + virtual bool forEachAtom(AtomHandler& h) const { h.doAtom(_atom); return true; } + virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const { return false; } + + void reserveFixups(unsigned int count) { _atom._fixups.reserve(count); } + void addSectionFixup(const ld::Fixup& f) { _atom._fixups.push_back(f); } + ld::Atom& atom() { return _atom; } +private: + friend class Atom; + + Atom _atom; + ld::Section _section; +}; + +Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) + : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), _name(strdup(n)), _content(content), _size(sz) {} + + + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +struct DTraceProbeInfo { + DTraceProbeInfo(const ld::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} + const ld::Atom* atom; + uint32_t offset; + const char* probeName; +}; +typedef __gnu_cxx::hash_map<const char*, std::vector<DTraceProbeInfo>, __gnu_cxx::hash<const char*>, CStringEquals> ProviderToProbes; +typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet; + + + +void doPass(const Options& opts, ld::Internal& internal) +{ + static bool log = false; + + // only make __dof section in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // scan all atoms looking for dtrace probes + std::vector<DTraceProbeInfo> probeSites; + std::vector<DTraceProbeInfo> isEnabledSites; + std::map<const ld::Atom*,CStringSet> atomToDtraceTypes; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeCode ) + continue; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); + break; + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); + break; + case ld::Fixup::kindDtraceExtra: + atomToDtraceTypes[atom].insert(fit->u.name); + break; + default: + break; + } + } + } + } + + // if no probes, we're done + if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) ) + return; + + ld::Fixup::Kind storeKind = ld::Fixup::kindNone; + switch ( opts.architecture() ) { + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + storeKind = ld::Fixup::kindStoreBigEndian32; + break; + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + case CPU_TYPE_ARM: + storeKind = ld::Fixup::kindStoreLittleEndian32; + break; + default: + throw "unsupported arch for DOF"; + } + + // partition probes by provider name + // The symbol names looks like: + // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] + // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] + ProviderToProbes providerToProbes; + std::vector<DTraceProbeInfo> emptyList; + for(std::vector<DTraceProbeInfo>::iterator it = probeSites.begin(); it != probeSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( it->atom->coalescedAway() ) + continue; + const char* providerStart = &it->probeName[16]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + for(std::vector<DTraceProbeInfo>::iterator it = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) { + // ignore probes in functions that were coalesed away rdar://problem/5628149 + if ( it->atom->coalescedAway() ) + continue; + const char* providerStart = &it->probeName[20]; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char providerName[providerEnd-providerStart+1]; + strlcpy(providerName, providerStart, providerEnd-providerStart+1); + ProviderToProbes::iterator pos = providerToProbes.find(providerName); + if ( pos == providerToProbes.end() ) { + const char* dup = strdup(providerName); + providerToProbes[dup] = emptyList; + } + providerToProbes[providerName].push_back(*it); + } + } + + // create a DOF section for each provider + int dofIndex=1; + CStringSet sectionNamesUsed; + for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { + const char* providerName = pit->first; + const std::vector<DTraceProbeInfo>& probes = pit->second; + + // open library and find dtrace_create_dof() + void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); + if ( handle == NULL ) + throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); + createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); + if ( pCreateDOF == NULL ) + throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); + // build list of typedefs/stability infos for this provider + CStringSet types; + for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) { + std::map<const ld::Atom*,CStringSet>::iterator pos = atomToDtraceTypes.find(it->atom); + if ( pos != atomToDtraceTypes.end() ) { + for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { + const char* providerStart = strchr(*sit, '$')+1; + const char* providerEnd = strchr(providerStart, '$'); + if ( providerEnd != NULL ) { + char aProviderName[providerEnd-providerStart+1]; + strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); + if ( strcmp(aProviderName, providerName) == 0 ) + types.insert(*sit); + } + } + } + } + int typeCount = types.size(); + const char* typeNames[typeCount]; + //fprintf(stderr, "types for %s:\n", providerName); + uint32_t index = 0; + for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { + typeNames[index] = *it; + //fprintf(stderr, "\t%s\n", *it); + ++index; + } + + // build list of probe/isenabled sites + const uint32_t probeCount = probes.size(); + const char* probeNames[probeCount]; + const char* funtionNames[probeCount]; + uint64_t offsetsInDOF[probeCount]; + index = 0; + for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) { + probeNames[index] = it->probeName; + funtionNames[index] = it->atom->name(); + offsetsInDOF[index] = 0; + ++index; + } + if ( log ) { + fprintf(stderr, "calling libtrace to create DOF:\n"); + fprintf(stderr, " types::\n"); + for(int i=0; i < typeCount; ++i) + fprintf(stderr, " [%u]\t %s\n", i, typeNames[i]); + fprintf(stderr, " probes::\n"); + for(uint32_t i=0; i < probeCount; ++i) + fprintf(stderr, " [%u]\t %s in %s\n", i, probeNames[i], funtionNames[i]); + } + + // call dtrace library to create DOF section + size_t dofSectionSize; + uint8_t* p = (*pCreateDOF)(opts.architecture(), typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); + if ( p != NULL ) { + char* sectionName = new char[18]; // alloc new string, pass ownership to File() + strcpy(sectionName, "__dof_"); + strlcpy(§ionName[6], providerName, 10); + // create unique section name so each DOF is in its own section + if ( sectionNamesUsed.count(sectionName) != 0 ) { + sectionName[15] = '0'; + sectionName[16] = '\0'; + while ( sectionNamesUsed.count(sectionName) != 0 ) { + ++sectionName[15]; + } + } + sectionNamesUsed.insert(sectionName); + char symbolName[strlen(providerName)+64]; + sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); + File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, 0, symbolName); + if ( log ) { + fprintf(stderr, "libdtrace created DOF of size %ld\n", dofSectionSize); + } + // add references + f->reserveFixups(3*probeCount); + for (uint32_t i=0; i < probeCount; ++i) { + uint64_t offset = offsetsInDOF[i]; + //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); + if ( offset > dofSectionSize ) + throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, probes[i].atom)); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, probes[i].offset)); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, &f->atom())); + f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k4of4, storeKind)); + } + // insert new section + internal.addAtom(f->atom()); + } + else { + throw "error creating dtrace DOF section"; + } + } + + + +} + + +} // namespace dtrace +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/dtrace_dof.h b/src/ld/passes/dtrace_dof.h new file mode 100644 index 0000000..df69b87 --- /dev/null +++ b/src/ld/passes/dtrace_dof.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __DTRACE_DOF_H__ +#define __DTRACE_DOF_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace dtrace { + +// called by linker to process atoms in Internal and add DOF sections as needed +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace dtrace +} // namespace passes +} // namespace ld + +#endif // __DTRACE_DOF_H__ diff --git a/src/ld/passes/dylibs.cpp b/src/ld/passes/dylibs.cpp new file mode 100644 index 0000000..2b4452b --- /dev/null +++ b/src/ld/passes/dylibs.cpp @@ -0,0 +1,86 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> +#include <mach/machine.h> + +#include <vector> + +#include "ld.hpp" +#include "dylibs.h" + +namespace ld { +namespace passes { +namespace dylibs { + + +class WillBeUsed +{ +public: + bool operator()(ld::dylib::File* dylib) const { + return dylib->willRemoved(); + } +}; + + +void doPass(const Options& opts, ld::Internal& state) +{ +// const bool log = false; + + // only optimize dylibs in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // clear "willRemoved" bit on all dylibs + for (std::vector<ld::dylib::File*>::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + aDylib->setWillBeRemoved(false); + } + for (std::vector<ld::dylib::File*>::iterator it = state.dylibs.begin(); it != state.dylibs.end(); ++it) { + ld::dylib::File* aDylib = *it; + // set "willRemoved" bit on implicit dylibs that did not provide any exports + if ( aDylib->implicitlyLinked() && !aDylib->explicitlyLinked() && !aDylib->providedExportAtom() ) + aDylib->setWillBeRemoved(true); + // set "willRemoved" bit on dead strippable explicit dylibs that did not provide any exports + if ( aDylib->explicitlyLinked() && aDylib->deadStrippable() && !aDylib->providedExportAtom() ) + aDylib->setWillBeRemoved(true); + // set "willRemoved" bit on any unused explicit when -dead_strip_dylibs is used + if ( opts.deadStripDylibs() && !aDylib->providedExportAtom() ) + aDylib->setWillBeRemoved(true); + } + + + // remove unused dylibs + state.dylibs.erase(std::remove_if(state.dylibs.begin(), state.dylibs.end(), WillBeUsed()), state.dylibs.end()); + +} + + +} // namespace dylibs +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/dylibs.h b/src/ld/passes/dylibs.h new file mode 100644 index 0000000..3e4db11 --- /dev/null +++ b/src/ld/passes/dylibs.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __DYLIBS_H__ +#define __DYLIBS_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace dylibs { + +// called by linker to optimize use of dylibs +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace dylibs +} // namespace passes +} // namespace ld + +#endif // __DYLIBS_H__ diff --git a/src/ld/passes/got.cpp b/src/ld/passes/got.cpp new file mode 100644 index 0000000..ff18e00 --- /dev/null +++ b/src/ld/passes/got.cpp @@ -0,0 +1,276 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> + +#include <vector> +#include <map> +#include <ext/hash_map> + +#include "ld.hpp" +#include "got.h" + +namespace ld { +namespace passes { +namespace got { + +class File; // forward reference + +class GOTEntryAtom : public ld::Atom { +public: + GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), + _target(target) + { _fixup.weakImport = weakImport; internal.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _target->name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + const ld::Atom* _target; + + static ld::Section _s_section; +}; + +ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable) +{ + switch (fixup->kind) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + // start by assuming this can be optimized + *optimizable = true; + // cannot do LEA optimization if target is in another dylib + if ( targetOfGOT->definition() == ld::Atom::definitionProxy ) + *optimizable = false; + // cannot do LEA optimization if target in __huge section + if ( internal.usingHugeSections && (targetOfGOT->size() > 1024*1024) + && ( (targetOfGOT->section().type() == ld::Section::typeZeroFill) + || (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) { + *optimizable = false; + } + if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { + // cannot do LEA optimization if target is weak exported symbol + if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) + *optimizable = false; + // cannot do LEA optimization if target is interposable + if ( opts.interposable(targetOfGOT->name()) ) + *optimizable = false; + // cannot do LEA optimization if target is resolver function + if ( targetOfGOT->contentType() == ld::Atom::typeResolver ) + *optimizable = false; + // cannot do LEA optimization for flat-namespace + if ( opts.nameSpace() != Options::kTwoLevelNameSpace ) + *optimizable = false; + } + return true; + case ld::Fixup::kindStoreX86PCRel32GOT: + *optimizable = false; + return true; + default: + break; + } + + return false; +} + +struct AtomByNameSorter +{ + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } +}; + +void doPass(const Options& opts, ld::Internal& internal) +{ + const bool log = false; + + // only make got section in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // walk all atoms and fixups looking for stubable references + // don't create stubs inline because that could invalidate the sections walk + std::vector<const ld::Atom*> atomsReferencingGOT; + std::map<const ld::Atom*,ld::Atom*> gotMap; + std::map<const ld::Atom*,bool> weakImportMap; + atomsReferencingGOT.reserve(128); + for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + bool atomUsesGOT = false; + const ld::Atom* targetOfGOT = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) + targetOfGOT = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + targetOfGOT = fit->u.target; + break; + default: + break; + } + bool optimizable; + if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + continue; + if ( optimizable ) { + // change from load of GOT entry to lea of target + if ( log ) fprintf(stderr, "optimized GOT usage in %s to %s\n", atom->name(), targetOfGOT->name()); + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + case ld::Fixup::bindingDirectlyBound: + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = targetOfGOT; + fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA; + break; + default: + assert(0 && "unsupported GOT reference"); + break; + } + } + else { + // remember that we need to use GOT in this function + if ( log ) fprintf(stderr, "found GOT use in %s to %s\n", atom->name(), targetOfGOT->name()); + if ( !atomUsesGOT ) { + atomsReferencingGOT.push_back(atom); + atomUsesGOT = true; + } + gotMap[targetOfGOT] = NULL; + // record weak_import attribute + std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(targetOfGOT); + if ( pos == weakImportMap.end() ) { + // target not in weakImportMap, so add + weakImportMap[targetOfGOT] = fit->weakImport; + // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(targetOfGOT->file()); + if ( dylib != NULL ) { + if ( fit->weakImport ) + (const_cast<ld::dylib::File*>(dylib))->setUsingWeakImportedSymbols(); + else + (const_cast<ld::dylib::File*>(dylib))->setUsingNonWeakImportedSymbols(); + } + } + else { + // target in weakImportMap, check for weakness mismatch + if ( pos->second != fit->weakImport ) { + // found mismatch + switch ( opts.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", targetOfGOT->name()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + } + } + } + } + + // make GOT entries + for (std::map<const ld::Atom*,ld::Atom*>::iterator it = gotMap.begin(); it != gotMap.end(); ++it) { + it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first]); + } + + // update atoms to use GOT entries + for (std::vector<const ld::Atom*>::iterator it=atomsReferencingGOT.begin(); it != atomsReferencingGOT.end(); ++it) { + const ld::Atom* atom = *it; + const ld::Atom* targetOfGOT = NULL; + ld::Fixup::iterator fitThatSetTarget = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + targetOfGOT = NULL; + fitThatSetTarget = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex]; + fitThatSetTarget = fit; + break; + case ld::Fixup::bindingDirectlyBound: + targetOfGOT = fit->u.target; + fitThatSetTarget = fit; + break; + default: + break; + } + bool optimizable; + if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + continue; + if ( !optimizable ) { + // GOT use not optimized away, update to bind to GOT entry + assert(fitThatSetTarget != NULL); + switch ( fitThatSetTarget->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + case ld::Fixup::bindingDirectlyBound: + fitThatSetTarget->binding = ld::Fixup::bindingDirectlyBound; + fitThatSetTarget->u.target = gotMap[targetOfGOT]; + break; + default: + assert(0 && "unsupported GOT reference"); + break; + } + } + } + } + + // sort new atoms so links are consistent + for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeNonLazyPointer ) { + std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter()); + } + } +} + + +} // namespace got +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/got.h b/src/ld/passes/got.h new file mode 100644 index 0000000..7aae0f8 --- /dev/null +++ b/src/ld/passes/got.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __GOT_H__ +#define __GOT_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace got { + +// called by linker to create GOT entries and optimize GOT loads into LEAs instead +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace got +} // namespace passes +} // namespace ld + +#endif // __GOT_H__ diff --git a/src/ld/passes/huge.cpp b/src/ld/passes/huge.cpp new file mode 100644 index 0000000..932122a --- /dev/null +++ b/src/ld/passes/huge.cpp @@ -0,0 +1,113 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> +#include <mach/machine.h> + +#include <vector> + +#include "ld.hpp" +#include "huge.h" + +namespace ld { +namespace passes { +namespace huge { + +class NullAtom +{ +public: + bool operator()(const ld::Atom* atom) const { + return (atom == NULL); + } +}; + +void doPass(const Options& opts, ld::Internal& state) +{ + const bool log = false; + + // only make make __huge section in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // only make make __huge section for x86_64 + if ( opts.architecture() != CPU_TYPE_X86_64 ) + return; + + // only needed if some (non-linkedit) atoms have an addresss >2GB from base address + state.usingHugeSections = false; + uint64_t address = 0; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typePageZero ) + continue; + if ( sect->type() == ld::Section::typeStack ) + continue; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( (address > 0x7FFFFFFFLL) && !sect->isSectionHidden() ) { + state.usingHugeSections = true; + if (log) fprintf(stderr, "atom: %s is >2GB (0x%09llX), so enabling huge mode\n", atom->name(), address); + break; + } + address += atom->size(); + } + if ( state.usingHugeSections ) + break; + } + if ( !state.usingHugeSections ) + return; + + // move all zero fill atoms that >1MB in size to a new __huge section + ld::Internal::FinalSection* hugeSection = state.getFinalSection(ld::Section("__DATA", "__huge", ld::Section::typeZeroFill)); + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect == hugeSection ) + continue; + if ( sect->type() == ld::Section::typeZeroFill ) { + bool movedSome = false; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->size() > 1024*1024 ) { + hugeSection->atoms.push_back(atom); + if (log) fprintf(stderr, "moved to __huge: %s, size=%llu\n", atom->name(), atom->size()); + *ait = NULL; // change atom to NULL for later bulk removal + movedSome = true; + } + } + if ( movedSome ) + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), NullAtom()), sect->atoms.end()); + } + } + + +} + + +} // namespace huge +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/huge.h b/src/ld/passes/huge.h new file mode 100644 index 0000000..77dc43e --- /dev/null +++ b/src/ld/passes/huge.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __HUGE_H__ +#define __HUGE_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace huge { + +// called by linker to move large zero-fill atoms to the __huge section +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace huge +} // namespace passes +} // namespace ld + +#endif // __HUGE_H__ diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp new file mode 100644 index 0000000..f420b9f --- /dev/null +++ b/src/ld/passes/objc.cpp @@ -0,0 +1,1174 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> +#include <mach/machine.h> + +#include <vector> +#include <map> +#include <set> + +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +#include "ld.hpp" +#include "objc.h" + +namespace ld { +namespace passes { +namespace objc { + + + +struct objc_image_info { + uint32_t version; // initially 0 + uint32_t flags; +}; + +#define OBJC_IMAGE_IS_REPLACEMENT (1<<0) +#define OBJC_IMAGE_SUPPORTS_GC (1<<1) +#define OBJC_IMAGE_REQUIRES_GC (1<<2) +#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) +#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) + + + +// +// This class is the 8 byte section containing ObjC flags +// +template <typename A> +class ObjCImageInfoAtom : public ld::Atom { +public: + ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, + bool compaction, bool objcReplacementClasses, bool abi2); + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "objc image info"; } + virtual uint64_t size() const { return sizeof(objc_image_info); } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + memcpy(buffer, &_content, sizeof(objc_image_info)); + } + +private: + objc_image_info _content; + + static ld::Section _s_sectionABI1; + static ld::Section _s_sectionABI2; +}; + +template <typename A> ld::Section ObjCImageInfoAtom<A>::_s_sectionABI1("__OBJC", "__image_info", ld::Section::typeUnclassified); +template <typename A> ld::Section ObjCImageInfoAtom<A>::_s_sectionABI2("__DATA", "__objc_imageinfo", ld::Section::typeUnclassified); + + +template <typename A> +ObjCImageInfoAtom<A>::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, + bool objcReplacementClasses, bool abi2) + : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) +{ + + uint32_t value = 0; + if ( objcReplacementClasses ) + value = OBJC_IMAGE_IS_REPLACEMENT; + switch ( objcConstraint ) { + case ld::File::objcConstraintNone: + case ld::File::objcConstraintRetainRelease: + if ( compaction ) + warning("ignoring -objc_gc_compaction because code not compiled for ObjC garbage collection"); + break; + case ld::File::objcConstraintRetainReleaseOrGC: + value |= OBJC_IMAGE_SUPPORTS_GC; + if ( compaction ) + value |= OBJC_IMAGE_SUPPORTS_COMPACTION; + break; + case ld::File::objcConstraintGC: + value |= OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC; + if ( compaction ) + value |= OBJC_IMAGE_SUPPORTS_COMPACTION; + break; + } + + _content.version = 0; + A::P::E::set32(_content.flags, value); +} + + + +// +// This class is for a new Atom which is an ObjC method list created by merging method lists from categories +// +template <typename A> +class MethodListAtom : public ld::Atom { +public: + MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta, + const std::vector<const ld::Atom*>* categories, + std::set<const ld::Atom*>& deadAtoms); + + virtual const ld::File* file() const { return _file; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "objc merged method list"; } + virtual uint64_t size() const { return _methodCount*3*sizeof(pint_t) + 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + bzero(buffer, size()); + A::P::E::set32(*((uint32_t*)(&buffer[0])), 24); + A::P::E::set32(*((uint32_t*)(&buffer[4])), _methodCount); + } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; } + +private: + typedef typename A::P::uint_t pint_t; + + const ld::File* _file; + unsigned int _methodCount; + std::vector<ld::Fixup> _fixups; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section MethodListAtom<A>::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified); + + +// +// This class is for a new Atom which is an ObjC protocol list created by merging protocol lists from categories +// +template <typename A> +class ProtocolListAtom : public ld::Atom { +public: + ProtocolListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, + const std::vector<const ld::Atom*>* categories, + std::set<const ld::Atom*>& deadAtoms); + + virtual const ld::File* file() const { return _file; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "objc merged protocol list"; } + virtual uint64_t size() const { return (_protocolCount+1)*sizeof(pint_t); } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + bzero(buffer, size()); + A::P::setP(*((pint_t*)(buffer)), _protocolCount); + } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; } + +private: + typedef typename A::P::uint_t pint_t; + + const ld::File* _file; + unsigned int _protocolCount; + std::vector<ld::Fixup> _fixups; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section ProtocolListAtom<A>::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified); + + + +// +// This class is for a new Atom which is an ObjC property list created by merging property lists from categories +// +template <typename A> +class PropertyListAtom : public ld::Atom { +public: + PropertyListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, + const std::vector<const ld::Atom*>* categories, + std::set<const ld::Atom*>& deadAtoms); + + virtual const ld::File* file() const { return _file; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return "objc merged property list"; } + virtual uint64_t size() const { return _propertyCount*2*sizeof(pint_t) + 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void setScope(Scope) { } + virtual void copyRawContent(uint8_t buffer[]) const { + bzero(buffer, size()); + A::P::E::set32(((uint32_t*)(buffer))[0], 2*sizeof(pint_t)); // sizeof(objc_property) + A::P::E::set32(((uint32_t*)(buffer))[1], _propertyCount); + } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; } + +private: + typedef typename A::P::uint_t pint_t; + + const ld::File* _file; + unsigned int _propertyCount; + std::vector<ld::Fixup> _fixups; + + static ld::Section _s_section; +}; + +template <typename A> +ld::Section PropertyListAtom<A>::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified); + + + + + +// +// This class is used to create an Atom that replaces an atom from a .o file that holds a class_ro_t. +// It is needed because there is no way to add Fixups to an existing atom. +// +template <typename A> +class ClassROOverlayAtom : public ld::Atom { +public: + ClassROOverlayAtom(const ld::Atom* classROAtom); + + // overrides of ld::Atom + virtual const ld::File* file() const { return _atom->file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return _atom->translationUnitSource(dir, nm); } + virtual const char* name() const { return _atom->name(); } + virtual uint64_t size() const { return _atom->size(); } + virtual uint64_t objectAddress() const { return _atom->objectAddress(); } + virtual void copyRawContent(uint8_t buffer[]) const + { _atom->copyRawContent(buffer); } + virtual const uint8_t* rawContentPointer() const + { return _atom->rawContentPointer(); } + virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const + { return _atom->contentHash(ibt); } + virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const + { return _atom->canCoalesceWith(rhs,ibt); } + + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } + virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; } + + void addProtocolListFixup(); + void addPropertyListFixup(); + void addMethodListFixup(); + +private: + typedef typename A::P::uint_t pint_t; + + const ld::Atom* _atom; + std::vector<ld::Fixup> _fixups; +}; + +template <typename A> +ClassROOverlayAtom<A>::ClassROOverlayAtom(const ld::Atom* classROAtom) + : ld::Atom(classROAtom->section(), ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + classROAtom->symbolTableInclusion(), false, false, false, classROAtom->alignment()), + _atom(classROAtom) +{ + // ensure all attributes are same as original + this->setAttributesFromAtom(*classROAtom); + + // copy fixups from orginal atom + for (ld::Fixup::iterator fit=classROAtom->fixupsBegin(); fit != classROAtom->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + _fixups.push_back(fixup); + } +} + + +// +// Base class for reading and updating existing ObjC atoms from .o files +// +template <typename A> +class ObjCData { +public: + static const ld::Atom* getPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, unsigned int offset, bool* hasAddend=NULL); + static void setPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, + unsigned int offset, const ld::Atom* newAtom); + typedef typename A::P::uint_t pint_t; +}; + +template <typename A> +const ld::Atom* ObjCData<A>::getPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, unsigned int offset, bool* hasAddend) +{ + const ld::Atom* target = NULL; + if ( hasAddend != NULL ) + *hasAddend = false; + for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) { + if ( fit->offsetInAtom == offset ) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingNone: + if ( fit->kind == ld::Fixup::kindAddAddend ) { + if ( hasAddend != NULL ) + *hasAddend = true; + } + break; + default: + break; + } + } + } + return target; +} + +template <typename A> +void ObjCData<A>::setPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, + unsigned int offset, const ld::Atom* newAtom) +{ + for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) { + if ( fit->offsetInAtom == offset ) { + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + state.indirectBindingTable[fit->u.bindingIndex] = newAtom; + return; + case ld::Fixup::bindingDirectlyBound: + fit->u.target = newAtom; + return; + default: + break; + } + } + } + assert(0 && "could not update method list"); +} + + + +// +// Helper class for reading and updating existing ObjC category atoms from .o files +// +template <typename A> +class Category : public ObjCData<A> { +public: + static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getClassMethods(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getProtocols(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getProperties(ld::Internal& state, const ld::Atom* contentAtom); + static uint32_t size() { return 6*sizeof(pint_t); } +private: + typedef typename A::P::uint_t pint_t; +}; + + +template <typename A> +const ld::Atom* Category<A>::getClass(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData<A>::getPointerInContent(state, contentAtom, sizeof(pint_t)); // category_t.cls +} + +template <typename A> +const ld::Atom* Category<A>::getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData<A>::getPointerInContent(state, contentAtom, 2*sizeof(pint_t)); // category_t.instanceMethods +} + +template <typename A> +const ld::Atom* Category<A>::getClassMethods(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData<A>::getPointerInContent(state, contentAtom, 3*sizeof(pint_t)); // category_t.classMethods +} + +template <typename A> +const ld::Atom* Category<A>::getProtocols(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData<A>::getPointerInContent(state, contentAtom, 4*sizeof(pint_t)); // category_t.protocols +} + +template <typename A> +const ld::Atom* Category<A>::getProperties(ld::Internal& state, const ld::Atom* contentAtom) +{ + return ObjCData<A>::getPointerInContent(state, contentAtom, 5*sizeof(pint_t)); // category_t.instanceProperties +} + + +template <typename A> +class MethodList : public ObjCData<A> { +public: + static uint32_t count(ld::Internal& state, const ld::Atom* methodListAtom) { + const uint32_t* methodListData = (uint32_t*)(methodListAtom->rawContentPointer()); + return A::P::E::get32(methodListData[1]); // method_list_t.count + } +}; + +template <typename A> +class ProtocolList : public ObjCData<A> { +public: + static uint32_t count(ld::Internal& state, const ld::Atom* protocolListAtom) { + pint_t* protocolListData = (pint_t*)(protocolListAtom->rawContentPointer()); + return A::P::getP(*protocolListData); // protocol_list_t.count + } +private: + typedef typename A::P::uint_t pint_t; +}; + +template <typename A> +class PropertyList : public ObjCData<A> { +public: + static uint32_t count(ld::Internal& state, const ld::Atom* protocolListAtom) { + uint32_t* protocolListData = (uint32_t*)(protocolListAtom->rawContentPointer()); + return A::P::E::get32(protocolListData[1]); // property_list_t.count + } +private: + typedef typename A::P::uint_t pint_t; +}; + + + +// +// Helper class for reading and updating existing ObjC class atoms from .o files +// +template <typename A> +class Class : public ObjCData<A> { +public: + static const ld::Atom* getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* getClassMethodList(ld::Internal& state, const ld::Atom* classAtom); + static const ld::Atom* setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms); + static const ld::Atom* setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms); + static const ld::Atom* setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms); + static const ld::Atom* setClassMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms); + static const ld::Atom* setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms); + static uint32_t size() { return 5*sizeof(pint_t); } + static unsigned int class_ro_header_size(); +private: + typedef typename A::P::uint_t pint_t; + static const ld::Atom* getROData(ld::Internal& state, const ld::Atom* classAtom); +}; + +template <> unsigned int Class<x86_64>::class_ro_header_size() { return 16; } +template <> unsigned int Class<arm>::class_ro_header_size() { return 12;} +template <> unsigned int Class<x86>::class_ro_header_size() { return 12; } + + +template <typename A> +const ld::Atom* Class<A>::getROData(ld::Internal& state, const ld::Atom* classAtom) +{ + return ObjCData<A>::getPointerInContent(state, classAtom, 4*sizeof(pint_t)); // class_t.data + +} + +template <typename A> +const ld::Atom* Class<A>::getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + return ObjCData<A>::getPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t)); // class_ro_t.baseMethods +} + +template <typename A> +const ld::Atom* Class<A>::getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + return ObjCData<A>::getPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t)); // class_ro_t.baseProtocols +} + +template <typename A> +const ld::Atom* Class<A>::getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + return ObjCData<A>::getPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t)); // class_ro_t.baseProperties +} + +template <typename A> +const ld::Atom* Class<A>::getClassMethodList(ld::Internal& state, const ld::Atom* classAtom) +{ + const ld::Atom* metaClassAtom = ObjCData<A>::getPointerInContent(state, classAtom, 0); // class_t.isa + assert(metaClassAtom != NULL); + return Class<A>::getInstanceMethodList(state, metaClassAtom); +} + +template <typename A> +const ld::Atom* Class<A>::setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + // if the base class does not already have a method list, we need to create an overlay + if ( getInstanceMethodList(state, classAtom) == NULL ) { + ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(classROAtom); + //fprintf(stderr, "replace class RO atom %p with %p for method list in class atom %s\n", classROAtom, overlay, classAtom->name()); + overlay->addMethodListFixup(); + ObjCData<A>::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data + deadAtoms.insert(classROAtom); + ObjCData<A>::setPointerInContent(state, overlay, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods + return overlay; + } + ObjCData<A>::setPointerInContent(state, classROAtom, class_ro_header_size() + 2*sizeof(pint_t), methodListAtom); // class_ro_t.baseMethods + return NULL; // means classRO atom was not replaced +} + +template <typename A> +const ld::Atom* Class<A>::setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + // if the base class does not already have a protocol list, we need to create an overlay + if ( getInstanceProtocolList(state, classAtom) == NULL ) { + ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(classROAtom); + //fprintf(stderr, "replace class RO atom %p with %p for protocol list in class atom %s\n", classROAtom, overlay, classAtom->name()); + overlay->addProtocolListFixup(); + ObjCData<A>::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data + deadAtoms.insert(classROAtom); + ObjCData<A>::setPointerInContent(state, overlay, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols + return overlay; + } + //fprintf(stderr, "set class RO atom %p protocol list in class atom %s\n", classROAtom, classAtom->name()); + ObjCData<A>::setPointerInContent(state, classROAtom, class_ro_header_size() + 3*sizeof(pint_t), protocolListAtom); // class_ro_t.baseProtocols + return NULL; // means classRO atom was not replaced +} + +template <typename A> +const ld::Atom* Class<A>::setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms) +{ + // meta class also points to same protocol list as class + const ld::Atom* metaClassAtom = ObjCData<A>::getPointerInContent(state, classAtom, 0); // class_t.isa + //fprintf(stderr, "setClassProtocolList(), classAtom=%p %s, metaClass=%p %s\n", classAtom, classAtom->name(), metaClassAtom, metaClassAtom->name()); + assert(metaClassAtom != NULL); + return setInstanceProtocolList(state, metaClassAtom, protocolListAtom, deadAtoms); +} + + + +template <typename A> +const ld::Atom* Class<A>::setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms) +{ + const ld::Atom* classROAtom = getROData(state, classAtom); // class_t.data + assert(classROAtom != NULL); + // if the base class does not already have a property list, we need to create an overlay + if ( getInstancePropertyList(state, classAtom) == NULL ) { + ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(classROAtom); + //fprintf(stderr, "replace class RO atom %p with %p for property list in class atom %s\n", classROAtom, overlay, classAtom->name()); + overlay->addPropertyListFixup(); + ObjCData<A>::setPointerInContent(state, classAtom, 4*sizeof(pint_t), overlay); // class_t.data + deadAtoms.insert(classROAtom); + ObjCData<A>::setPointerInContent(state, overlay, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties + return overlay; + } + ObjCData<A>::setPointerInContent(state, classROAtom, class_ro_header_size() + 6*sizeof(pint_t), propertyListAtom); // class_ro_t.baseProperties + return NULL; // means classRO atom was not replaced +} + +template <typename A> +const ld::Atom* Class<A>::setClassMethodList(ld::Internal& state, const ld::Atom* classAtom, + const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms) +{ + // class methods is just instance methods of metaClass + const ld::Atom* metaClassAtom = ObjCData<A>::getPointerInContent(state, classAtom, 0); // class_t.isa + assert(metaClassAtom != NULL); + return setInstanceMethodList(state, metaClassAtom, methodListAtom, deadAtoms); +} + + + +template <> +void ClassROOverlayAtom<x86_64>::addMethodListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<x86_64>::class_ro_header_size() + 2*8; // class_ro_t.baseMethods + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); +} + +template <> +void ClassROOverlayAtom<arm>::addMethodListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<arm>::class_ro_header_size() + 2*4; // class_ro_t.baseMethods + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + +template <> +void ClassROOverlayAtom<x86>::addMethodListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<x86>::class_ro_header_size() + 2*4; // class_ro_t.baseMethods + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + + + +template <> +void ClassROOverlayAtom<x86_64>::addProtocolListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<x86_64>::class_ro_header_size() + 3*8; // class_ro_t.baseProtocols + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); +} + +template <> +void ClassROOverlayAtom<arm>::addProtocolListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<arm>::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + +template <> +void ClassROOverlayAtom<x86>::addProtocolListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<x86>::class_ro_header_size() + 3*4; // class_ro_t.baseProtocols + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + + +template <> +void ClassROOverlayAtom<x86_64>::addPropertyListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<x86_64>::class_ro_header_size() + 6*8; // class_ro_t.baseProperties + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, targetAtom)); +} + +template <> +void ClassROOverlayAtom<arm>::addPropertyListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<arm>::class_ro_header_size() + 6*4; // class_ro_t.baseProperties + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + +template <> +void ClassROOverlayAtom<x86>::addPropertyListFixup() +{ + const ld::Atom* targetAtom = this; // temporary + uint32_t offset = Class<x86>::class_ro_header_size() + 6*4; // class_ro_t.baseProperties + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, targetAtom)); +} + + + + +// +// Encapsulates merging of ObjC categories +// +template <typename A> +class OptimizeCategories { +public: + static void doit(const Options& opts, ld::Internal& state); + static bool hasInstanceMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories); + static bool hasClassMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories); + static bool hasProtocols(ld::Internal& state, const std::vector<const ld::Atom*>* categories); + static bool hasProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories); + + + static unsigned int class_ro_baseMethods_offset(); +private: + typedef typename A::P::uint_t pint_t; + +}; + + +template <typename A> +bool OptimizeCategories<A>::hasInstanceMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories) +{ + for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* methodList = Category<A>::getInstanceMethods(state, categoryAtom); + if ( methodList != NULL ) { + if ( MethodList<A>::count(state, methodList) > 0 ) + return true; + } + } + return false; +} + + +template <typename A> +bool OptimizeCategories<A>::hasClassMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories) +{ + for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* methodList = Category<A>::getClassMethods(state, categoryAtom); + if ( methodList != NULL ) { + if ( MethodList<A>::count(state, methodList) > 0 ) + return true; + } + } + return false; +} + +template <typename A> +bool OptimizeCategories<A>::hasProtocols(ld::Internal& state, const std::vector<const ld::Atom*>* categories) +{ + for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* protocolListAtom = Category<A>::getProtocols(state, categoryAtom); + if ( protocolListAtom != NULL ) { + if ( ProtocolList<A>::count(state, protocolListAtom) > 0 ) { + return true; + } + } + } + return false; +} + + +template <typename A> +bool OptimizeCategories<A>::hasProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories) +{ + for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryAtom = *it; + const ld::Atom* propertyListAtom = Category<A>::getProperties(state, categoryAtom); + if ( propertyListAtom != NULL ) { + if ( PropertyList<A>::count(state, propertyListAtom) > 0 ) + return true; + } + } + return false; +} + + + +// +// Helper for std::remove_if +// +class OptimizedAway { +public: + OptimizedAway(const std::set<const ld::Atom*>& oa) : _dead(oa) {} + bool operator()(const ld::Atom* atom) const { + return ( _dead.count(atom) != 0 ); + } +private: + const std::set<const ld::Atom*>& _dead; +}; + +template <typename A> +void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state) +{ + // first find all categories referenced by __objc_nlcatlist section + std::set<const ld::Atom*> nlcatListAtoms; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) { + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* categoryListElementAtom = *ait; + for (unsigned int offset=0; offset < categoryListElementAtom->size(); offset += sizeof(pint_t)) { + const ld::Atom* categoryAtom = ObjCData<A>::getPointerInContent(state, categoryListElementAtom, offset); + //fprintf(stderr, "offset=%d, cat=%p %s\n", offset, categoryAtom, categoryAtom->name()); + assert(categoryAtom != NULL); + nlcatListAtoms.insert(categoryAtom); + } + } + } + } + + // build map of all classes in this image that have categories on them + typedef std::map<const ld::Atom*, std::vector<const ld::Atom*>*> CatMap; + CatMap classToCategories; + std::set<const ld::Atom*> deadAtoms; + ld::Internal::FinalSection* methodListSection = NULL; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeObjC2CategoryList ) { + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* categoryListElementAtom = *ait; + bool hasAddend; + const ld::Atom* categoryAtom = ObjCData<A>::getPointerInContent(state, categoryListElementAtom, 0, &hasAddend); + if ( hasAddend || (categoryAtom->symbolTableInclusion() == ld::Atom::symbolTableNotIn)) { + //<rdar://problem/8309530> gcc-4.0 uses 'L' labels on categories which disables this optimization + //warning("__objc_catlist element does not point to start of category"); + continue; + } + assert(categoryAtom != NULL); + assert(categoryAtom->size() == Category<A>::size()); + // ignore categories also in __objc_nlcatlist + if ( nlcatListAtoms.count(categoryAtom) != 0 ) + continue; + const ld::Atom* categoryOnClassAtom = Category<A>::getClass(state, categoryAtom); + assert(categoryOnClassAtom != NULL); + if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) { + // only look at classes defined in this image + CatMap::iterator pos = classToCategories.find(categoryOnClassAtom); + if ( pos == classToCategories.end() ) { + classToCategories[categoryOnClassAtom] = new std::vector<const ld::Atom*>(); + } + classToCategories[categoryOnClassAtom]->push_back(categoryAtom); + // mark category atom and catlist atom as dead + deadAtoms.insert(categoryAtom); + deadAtoms.insert(categoryListElementAtom); + } + } + } + // record method list section + if ( (strcmp(sect->sectionName(), "__objc_const") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + methodListSection = sect; + } + + // if found some categories + if ( classToCategories.size() != 0 ) { + assert(methodListSection != NULL); + // alter each class definition to have new method list which includes all category methods + for (CatMap::iterator it=classToCategories.begin(); it != classToCategories.end(); ++it) { + const ld::Atom* classAtom = it->first; + const std::vector<const ld::Atom*>* categories = it->second; + assert(categories->size() != 0); + // if any category adds instance methods, generate new merged method list, and replace + if ( OptimizeCategories<A>::hasInstanceMethods(state, categories) ) { + const ld::Atom* baseInstanceMethodListAtom = Class<A>::getInstanceMethodList(state, classAtom); + const ld::Atom* newInstanceMethodListAtom = new MethodListAtom<A>(state, baseInstanceMethodListAtom, false, categories, deadAtoms); + const ld::Atom* newClassRO = Class<A>::setInstanceMethodList(state, classAtom, newInstanceMethodListAtom, deadAtoms); + // add new method list to final sections + methodListSection->atoms.push_back(newInstanceMethodListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + } + // if any category adds class methods, generate new merged method list, and replace + if ( OptimizeCategories<A>::hasClassMethods(state, categories) ) { + const ld::Atom* baseClassMethodListAtom = Class<A>::getClassMethodList(state, classAtom); + const ld::Atom* newClassMethodListAtom = new MethodListAtom<A>(state, baseClassMethodListAtom, true, categories, deadAtoms); + const ld::Atom* newClassRO = Class<A>::setClassMethodList(state, classAtom, newClassMethodListAtom, deadAtoms); + // add new method list to final sections + methodListSection->atoms.push_back(newClassMethodListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + } + // if any category adds protocols, generate new merged protocol list, and replace + if ( OptimizeCategories<A>::hasProtocols(state, categories) ) { + const ld::Atom* baseProtocolListAtom = Class<A>::getInstanceProtocolList(state, classAtom); + const ld::Atom* newProtocolListAtom = new ProtocolListAtom<A>(state, baseProtocolListAtom, categories, deadAtoms); + const ld::Atom* newClassRO = Class<A>::setInstanceProtocolList(state, classAtom, newProtocolListAtom, deadAtoms); + const ld::Atom* newMetaClassRO = Class<A>::setClassProtocolList(state, classAtom, newProtocolListAtom, deadAtoms); + // add new protocol list to final sections + methodListSection->atoms.push_back(newProtocolListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + if ( newMetaClassRO != NULL ) { + assert(strcmp(newMetaClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newMetaClassRO); + } + } + // if any category adds properties, generate new merged property list, and replace + if ( OptimizeCategories<A>::hasProperties(state, categories) ) { + const ld::Atom* basePropertyListAtom = Class<A>::getInstancePropertyList(state, classAtom); + const ld::Atom* newPropertyListAtom = new PropertyListAtom<A>(state, basePropertyListAtom, categories, deadAtoms); + const ld::Atom* newClassRO = Class<A>::setInstancePropertyList(state, classAtom, newPropertyListAtom, deadAtoms); + // add new property list to final sections + methodListSection->atoms.push_back(newPropertyListAtom); + if ( newClassRO != NULL ) { + assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); + methodListSection->atoms.push_back(newClassRO); + } + } + + } + + // remove dead atoms + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), OptimizedAway(deadAtoms)), sect->atoms.end()); + } + } +} + + +template <typename A> +MethodListAtom<A>::MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta, + const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _methodCount(0) +{ + unsigned int fixupCount = 0; + // if base class has method list, then associate new method list with file defining class + if ( baseMethodList != NULL ) { + _file = baseMethodList->file(); + // calculate total size of merge method lists + _methodCount = MethodList<A>::count(state, baseMethodList); + deadAtoms.insert(baseMethodList); + fixupCount = baseMethodList->fixupsEnd() - baseMethodList->fixupsBegin(); + } + for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { + const ld::Atom* categoryMethodListAtom; + if ( meta ) + categoryMethodListAtom = Category<A>::getClassMethods(state, *ait); + else + categoryMethodListAtom = Category<A>::getInstanceMethods(state, *ait); + if ( categoryMethodListAtom != NULL ) { + _methodCount += MethodList<A>::count(state, categoryMethodListAtom); + fixupCount += (categoryMethodListAtom->fixupsEnd() - categoryMethodListAtom->fixupsBegin()); + deadAtoms.insert(categoryMethodListAtom); + // if base class did not have method list, associate new method list with file the defined category + if ( _file == NULL ) + _file = categoryMethodListAtom->file(); + } + } + //if ( baseMethodList != NULL ) + // fprintf(stderr, "total merged method count=%u for baseMethodList=%s\n", _methodCount, baseMethodList->name()); + //else + // fprintf(stderr, "total merged method count=%u\n", _methodCount); + //fprintf(stderr, "total merged fixup count=%u\n", fixupCount); + + // copy fixups and adjust offsets (in reverse order to simulator objc runtime) + _fixups.reserve(fixupCount); + uint32_t slide = 0; + for (std::vector<const ld::Atom*>::const_reverse_iterator rit=categories->rbegin(); rit != categories->rend(); ++rit) { + const ld::Atom* categoryMethodListAtom; + if ( meta ) + categoryMethodListAtom = Category<A>::getClassMethods(state, *rit); + else + categoryMethodListAtom = Category<A>::getInstanceMethods(state, *rit); + if ( categoryMethodListAtom != NULL ) { + for (ld::Fixup::iterator fit=categoryMethodListAtom->fixupsBegin(); fit != categoryMethodListAtom->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + //if ( fixup.binding == ld::Fixup::bindingDirectlyBound ) + // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name()); + } + slide += 3*sizeof(pint_t) * MethodList<A>::count(state, categoryMethodListAtom); + } + } + // add method list from base class last + if ( baseMethodList != NULL ) { + for (ld::Fixup::iterator fit=baseMethodList->fixupsBegin(); fit != baseMethodList->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + } + } +} + + +template <typename A> +ProtocolListAtom<A>::ProtocolListAtom(ld::Internal& state, const ld::Atom* baseProtocolList, + const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _protocolCount(0) +{ + unsigned int fixupCount = 0; + if ( baseProtocolList != NULL ) { + // if base class has protocol list, then associate new protocol list with file defining class + _file = baseProtocolList->file(); + // calculate total size of merged protocol list + _protocolCount = ProtocolList<A>::count(state, baseProtocolList); + deadAtoms.insert(baseProtocolList); + fixupCount = baseProtocolList->fixupsEnd() - baseProtocolList->fixupsBegin(); + } + for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { + const ld::Atom* categoryProtocolListAtom = Category<A>::getProtocols(state, *ait); + if ( categoryProtocolListAtom != NULL ) { + _protocolCount += ProtocolList<A>::count(state, categoryProtocolListAtom); + fixupCount += (categoryProtocolListAtom->fixupsEnd() - categoryProtocolListAtom->fixupsBegin()); + deadAtoms.insert(categoryProtocolListAtom); + // if base class did not have protocol list, associate new protocol list with file the defined category + if ( _file == NULL ) + _file = categoryProtocolListAtom->file(); + } + } + //fprintf(stderr, "total merged protocol count=%u\n", _protocolCount); + //fprintf(stderr, "total merged fixup count=%u\n", fixupCount); + + // copy fixups and adjust offsets + _fixups.reserve(fixupCount); + uint32_t slide = 0; + for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryProtocolListAtom = Category<A>::getProtocols(state, *it); + if ( categoryProtocolListAtom != NULL ) { + for (ld::Fixup::iterator fit=categoryProtocolListAtom->fixupsBegin(); fit != categoryProtocolListAtom->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + //if ( fixup.binding == ld::Fixup::bindingDirectlyBound ) + // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name()); + } + slide += sizeof(pint_t) * ProtocolList<A>::count(state, categoryProtocolListAtom); + } + } + // add method list from base class last + if ( baseProtocolList != NULL ) { + for (ld::Fixup::iterator fit=baseProtocolList->fixupsBegin(); fit != baseProtocolList->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + } + } +} + + +template <typename A> +PropertyListAtom<A>::PropertyListAtom(ld::Internal& state, const ld::Atom* basePropertyList, + const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _propertyCount(0) +{ + unsigned int fixupCount = 0; + if ( basePropertyList != NULL ) { + // if base class has property list, then associate new property list with file defining class + _file = basePropertyList->file(); + // calculate total size of merged property list + _propertyCount = PropertyList<A>::count(state, basePropertyList); + deadAtoms.insert(basePropertyList); + fixupCount = basePropertyList->fixupsEnd() - basePropertyList->fixupsBegin(); + } + for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { + const ld::Atom* categoryPropertyListAtom = Category<A>::getProperties(state, *ait); + if ( categoryPropertyListAtom != NULL ) { + _propertyCount += PropertyList<A>::count(state, categoryPropertyListAtom); + fixupCount += (categoryPropertyListAtom->fixupsEnd() - categoryPropertyListAtom->fixupsBegin()); + deadAtoms.insert(categoryPropertyListAtom); + // if base class did not have property list, associate new property list with file the defined category + if ( _file == NULL ) + _file = categoryPropertyListAtom->file(); + } + } + //fprintf(stderr, "total merged property count=%u\n", _propertyCount); + //fprintf(stderr, "total merged fixup count=%u\n", fixupCount); + + // copy fixups and adjust offsets + _fixups.reserve(fixupCount); + uint32_t slide = 0; + for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) { + const ld::Atom* categoryPropertyListAtom = Category<A>::getProperties(state, *it); + if ( categoryPropertyListAtom != NULL ) { + for (ld::Fixup::iterator fit=categoryPropertyListAtom->fixupsBegin(); fit != categoryPropertyListAtom->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + //fprintf(stderr, "offset=0x%08X, binding=%d\n", fixup.offsetInAtom, fixup.binding); + //if ( fixup.binding == ld::Fixup::bindingDirectlyBound ) + // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name()); + //else if ( fixup.binding == ld::Fixup::bindingsIndirectlyBound ) + // fprintf(stderr, "offset=0x%08X, indirect index=%u, name=%s\n", fixup.offsetInAtom, fixup.u.bindingIndex, + // (char*)(state.indirectBindingTable[fixup.u.bindingIndex]->rawContentPointer())); + } + slide += 2*sizeof(pint_t) * PropertyList<A>::count(state, categoryPropertyListAtom); + } + } + // add method list from base class last + if ( basePropertyList != NULL ) { + for (ld::Fixup::iterator fit=basePropertyList->fixupsBegin(); fit != basePropertyList->fixupsEnd(); ++fit) { + ld::Fixup fixup = *fit; + fixup.offsetInAtom += slide; + _fixups.push_back(fixup); + } + } +} + + + + +void doPass(const Options& opts, ld::Internal& state) +{ + // only make image info section if objc was used + if ( state.objcObjectConstraint != ld::File::objcConstraintNone ) { + + // verify dylibs are GC compatible with object files + if ( state.objcObjectConstraint != state.objcDylibConstraint ) { + if ( (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease) + && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) { + throw "Linked dylibs built for retain/release but object files built for GC-only"; + } + else if ( (state.objcDylibConstraint == ld::File::objcConstraintGC) + && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) { + throw "Linked dylibs built for GC-only but object files built for retain/release"; + } + } + + const bool compaction = opts.objcGcCompaction(); + + // add image info atom + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + state.addAtom(*new ObjCImageInfoAtom<x86_64>(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, true)); + break; + case CPU_TYPE_I386: + state.addAtom(*new ObjCImageInfoAtom<x86>(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, opts.objCABIVersion2POverride() ? true : false)); + break; + case CPU_TYPE_POWERPC: + state.addAtom(*new ObjCImageInfoAtom<ppc>(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, false)); + break; + case CPU_TYPE_ARM: + state.addAtom(*new ObjCImageInfoAtom<arm>(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, true)); + break; + case CPU_TYPE_POWERPC64: + state.addAtom(*new ObjCImageInfoAtom<ppc64>(state.objcObjectConstraint, compaction, + state.hasObjcReplacementClasses, true)); + break; + default: + assert(0 && "unknown objc arch"); + } + } + + if ( opts.objcCategoryMerging() ) { + // optimize classes defined in this linkage unit by merging in categories also in this linkage unit + switch ( opts.architecture() ) { + case CPU_TYPE_X86_64: + OptimizeCategories<x86_64>::doit(opts, state); + break; + case CPU_TYPE_I386: + // disable optimization until fully tested + //if ( opts.objCABIVersion2POverride() ) + // OptimizeCategories<x86>::doit(opts, state); + break; + case CPU_TYPE_ARM: + // disable optimization until fully tested + //OptimizeCategories<arm>::doit(opts, state); + break; + case CPU_TYPE_POWERPC64: + case CPU_TYPE_POWERPC: + break; + default: + assert(0 && "unknown objc arch"); + } + } +} + + +} // namespace objc +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/objc.h b/src/ld/passes/objc.h new file mode 100644 index 0000000..d92def6 --- /dev/null +++ b/src/ld/passes/objc.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __OBJC_H__ +#define __OBJC_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace objc { + +// called by linker to optimize ObjC data structures +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace objc +} // namespace passes +} // namespace ld + +#endif // __OBJC_H__ diff --git a/src/ld/passes/order_file.cpp b/src/ld/passes/order_file.cpp new file mode 100644 index 0000000..9e336ae --- /dev/null +++ b/src/ld/passes/order_file.cpp @@ -0,0 +1,531 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> +#include <mach/machine.h> + +#include <vector> +#include <map> + +#include "ld.hpp" +#include "order_file.h" + +namespace ld { +namespace passes { +namespace order_file { + +// +// The purpose of this pass is to take the graph of all Atoms and produce an ordered +// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must +// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified +// in an order_file are sequenced as in the order_file and before Atoms not specified, +// 4) Atoms in the same section from the same .o file should be contiguous and sequenced +// in the same order they were in the .o file, 5) Atoms in the same Section but which came +// from different .o files should be sequenced in the same order that the .o files +// were passed to the linker (i.e. command line order). +// +// The way this is implemented is that the linker passes a "base ordinal" to each File +// as it is constructed. Add each atom has an objectAddress() method. Then +// sorting is just sorting by section, then by file ordinal, then by object address. +// +// If an order_file is specified, it gets more complicated. First, an override-ordinal map +// is created. It causes the sort routine to ignore the value returned by ordinal() and objectAddress() +// and use the override value instead. Next some Atoms must be layed out consecutively +// (e.g. hand written assembly that does not end with return, but rather falls into +// the next label). This is modeled in via a kindNoneFollowOn fixup. The use of +// kindNoneFollowOn fixups produces "clusters" of atoms that must stay together. +// If an order_file tries to move one atom, it may need to move a whole cluster. The +// algorithm to do this models clusters using two maps. The "starts" maps maps any +// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a +// cluster to the next Atom in the cluster. With this in place, while processing an +// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is +// given ordinal overrides. +// + +class Layout +{ +public: + Layout(const Options& opts, ld::Internal& state); + void doPass(); +private: + + class Comparer { + public: + Comparer(const Layout& l) : _layout(l) {} + bool operator()(const ld::Atom* left, const ld::Atom* right); + private: + const Layout& _layout; + }; + + class CStringEquals { + public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map<const char*, const ld::Atom*, __gnu_cxx::hash<const char*>, CStringEquals> NameToAtom; + + typedef std::map<const ld::Atom*, const ld::Atom*> AtomToAtom; + + typedef std::map<const ld::Atom*, uint32_t> AtomToOrdinal; + + const ld::Atom* findAtom(const Options::OrderedSymbol& orderedSymbol); + void buildNameTable(); + void buildFollowOnTables(); + void buildOrdinalOverrideMap(); + const ld::Atom* follower(const ld::Atom* atom); + static bool matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafName); + bool orderableSection(const ld::Internal::FinalSection*); + + const Options& _options; + ld::Internal& _state; + AtomToAtom _followOnStarts; + AtomToAtom _followOnNexts; + NameToAtom _nameTable; + std::vector<const ld::Atom*> _nameCollisionAtoms; + AtomToOrdinal _ordinalOverrideMap; + Comparer _comparer; + bool _haveOrderFile; + + static bool _s_log; +}; + +bool Layout::_s_log = false; + +Layout::Layout(const Options& opts, ld::Internal& state) + : _options(opts), _state(state), _comparer(*this), _haveOrderFile(opts.orderedSymbolsCount() != 0) +{ +} + + +bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) +{ + if ( left == right ) + return false; + + // magic section$start symbol always sorts to the start of its section + if ( left->contentType() == ld::Atom::typeSectionStart ) + return true; + if ( right->contentType() == ld::Atom::typeSectionStart ) + return false; + + // if a -order_file is specified, then sorting is altered to sort those symbols first + if ( _layout._haveOrderFile ) { + AtomToOrdinal::const_iterator leftPos = _layout._ordinalOverrideMap.find(left); + AtomToOrdinal::const_iterator rightPos = _layout._ordinalOverrideMap.find(right); + AtomToOrdinal::const_iterator end = _layout._ordinalOverrideMap.end(); + if ( leftPos != end ) { + if ( rightPos != end ) { + // both left and right are overridden, so compare overridden ordinals + return leftPos->second < rightPos->second; + } + else { + // left is overridden and right is not, so left < right + return true; + } + } + else { + if ( rightPos != end ) { + // right is overridden and left is not, so right < left + return false; + } + else { + // neither are overridden, + // fall into default sorting below + } + } + } + + // magic section$end symbol always sorts to the end of its section + if ( left->contentType() == ld::Atom::typeSectionEnd ) + return false; + if ( right->contentType() == ld::Atom::typeSectionEnd ) + return true; + + // the __common section can have real or tentative definitions + // we want the real ones to sort before tentative ones + bool leftIsTent = (left->definition() == ld::Atom::definitionTentative); + bool rightIsTent = (right->definition() == ld::Atom::definitionTentative); + if ( leftIsTent != rightIsTent ) + return rightIsTent; + +#if 0 + // initializers are auto sorted to start of section + if ( !fInitializerSet.empty() ) { + bool leftFirst = (fInitializerSet.count(left) != 0); + bool rightFirst = (fInitializerSet.count(right) != 0); + if ( leftFirst != rightFirst ) + return leftFirst; + } + + // terminators are auto sorted to end of section + if ( !fTerminatorSet.empty() ) { + bool leftLast = (fTerminatorSet.count(left) != 0); + bool rightLast = (fTerminatorSet.count(right) != 0); + if ( leftLast != rightLast ) + return rightLast; + } +#endif + + // sort by .o order + uint32_t leftFileOrdinal = left->file()->ordinal(); + uint32_t rightFileOrdinal = right->file()->ordinal(); + if ( leftFileOrdinal != rightFileOrdinal ) + return leftFileOrdinal< rightFileOrdinal; + + // tentative defintions have no address in .o file, they are traditionally laid out by name + if ( leftIsTent && rightIsTent ) + return (strcmp(left->name(), right->name()) < 0); + + // lastly sort by atom address + int64_t addrDiff = left->objectAddress() - right->objectAddress(); + if ( addrDiff == 0 ) { + // have same address so one might be an alias, and aliases need to sort before target + bool leftIsAlias = left->isAlias(); + bool rightIsAlias = right->isAlias(); + if ( leftIsAlias != rightIsAlias ) + return leftIsAlias; + + // both at same address, sort by name + return (strcmp(left->name(), right->name()) < 0); + } + return (addrDiff < 0); +} + +bool Layout::matchesObjectFile(const ld::Atom* atom, const char* objectFileLeafName) +{ + if ( objectFileLeafName == NULL ) + return true; + const char* atomFullPath = atom->file()->path(); + const char* lastSlash = strrchr(atomFullPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 ) + return true; + } + else { + if ( strcmp(atomFullPath, objectFileLeafName) == 0 ) + return true; + } + return false; +} + + +bool Layout::orderableSection(const ld::Internal::FinalSection* sect) +{ + // atoms in only some sections are ordered + switch ( sect->type() ) { + case ld::Section::typeUnclassified: + case ld::Section::typeCode: + case ld::Section::typeZeroFill: + return true; + case ld::Section::typeImportProxies: + return false; + default: + // if section has command line aliases, then we must apply ordering so aliases layout before targets + if ( _options.haveCmdLineAliases() ) { + for (std::vector<const ld::Atom*>::const_iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->isAlias() ) + return true; + } + } + break; + } + return false; +} + +void Layout::buildNameTable() +{ + for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + // atoms in some special sections are never ordered + if ( ! orderableSection(sect) ) + continue; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->symbolTableInclusion() == ld::Atom::symbolTableIn ) { + const char* name = atom->name(); + if ( name != NULL) { + // static function or data + NameToAtom::iterator pos = _nameTable.find(name); + if ( pos == _nameTable.end() ) + _nameTable[name] = atom; + else { + _nameTable[name] = NULL; // collision, denote with NULL + _nameCollisionAtoms.push_back(atom); + } + } + } + } + } +} + + +const ld::Atom* Layout::findAtom(const Options::OrderedSymbol& orderedSymbol) +{ + // look for name in _nameTable + NameToAtom::iterator pos = _nameTable.find(orderedSymbol.symbolName); + if ( pos != _nameTable.end() ) { + if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) { + //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName); + return pos->second; + } + if ( pos->second == NULL ) { + // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way + for (std::vector<const ld::Atom*>::iterator it=_nameCollisionAtoms.begin(); it != _nameCollisionAtoms.end(); it++) { + const ld::Atom* atom = *it; + if ( strcmp(atom->name(), orderedSymbol.symbolName) == 0 ) { + if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) { + if ( _options.printOrderFileStatistics() ) + warning("%s specified in order_file but it exists in multiple .o files. " + "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName); + return atom; + } + } + } + } + } + + return NULL; +} + +const ld::Atom* Layout::follower(const ld::Atom* atom) +{ + for (const ld::Atom* a = _followOnStarts[atom]; a != NULL; a = _followOnNexts[a]) { + assert(a != NULL); + if ( _followOnNexts[a] == atom ) { + return a; + } + } + // no follower, first in chain + return NULL; +} + +void Layout::buildFollowOnTables() +{ + // first make a pass to find all follow-on references and build start/next maps + // which are a way to represent clusters of atoms that must layout together + for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( !orderableSection(sect) ) + continue; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + const ld::Atom* followOnAtom = fit->u.target; + if ( _s_log ) fprintf(stderr, "ref %p %s -> %p %s\n", atom, atom->name(), followOnAtom, followOnAtom->name()); + assert(_followOnNexts.count(atom) == 0); + _followOnNexts[atom] = followOnAtom; + if ( _followOnStarts.count(atom) == 0 ) { + // first time atom has been seen, make it start of chain + _followOnStarts[atom] = atom; + if ( _s_log ) fprintf(stderr, " start %s -> %s\n", atom->name(), atom->name()); + } + if ( _followOnStarts.count(followOnAtom) == 0 ) { + // first time followOnAtom has been seen, make atom start of chain + _followOnStarts[followOnAtom] = _followOnStarts[atom]; + if ( _s_log ) fprintf(stderr, " start %s -> %s\n", followOnAtom->name(), _followOnStarts[atom]->name()); + } + else { + if ( _followOnStarts[followOnAtom] == followOnAtom ) { + // followOnAtom atom already start of another chain, hook together + // and change all to use atom as start + const ld::Atom* a = followOnAtom; + while ( true ) { + assert(_followOnStarts[a] == followOnAtom); + _followOnStarts[a] = _followOnStarts[atom]; + if ( _s_log ) fprintf(stderr, " adjust start for %s -> %s\n", a->name(), _followOnStarts[atom]->name()); + AtomToAtom::iterator pos = _followOnNexts.find(a); + if ( pos != _followOnNexts.end() ) + a = pos->second; + else + break; + } + } + else { + // attempt to insert atom into existing followOn chain + const ld::Atom* curPrevToFollowOnAtom = this->follower(followOnAtom); + assert(curPrevToFollowOnAtom != NULL); + assert((atom->size() == 0) || (curPrevToFollowOnAtom->size() == 0)); + if ( atom->size() == 0 ) { + // insert alias into existing chain right before followOnAtom + _followOnNexts[curPrevToFollowOnAtom] = atom; + _followOnNexts[atom] = followOnAtom; + _followOnStarts[atom] = _followOnStarts[followOnAtom]; + } + else { + // insert real atom into existing chain right before alias of followOnAtom + const ld::Atom* curPrevPrevToFollowOn = this->follower(curPrevToFollowOnAtom); + if ( curPrevPrevToFollowOn == NULL ) { + // nothing previous, so make this a start of a new chain + _followOnNexts[atom] = curPrevToFollowOnAtom; + for (const ld::Atom* a = atom; a != NULL; a = _followOnNexts[a]) { + if ( _s_log ) fprintf(stderr, " adjust start for %s -> %s\n", a->name(), atom->name()); + _followOnStarts[a] = atom; + } + } + else { + // is previous, insert into existing chain before previous + _followOnNexts[curPrevPrevToFollowOn] = atom; + _followOnNexts[atom] = curPrevToFollowOnAtom; + _followOnStarts[atom] = _followOnStarts[curPrevToFollowOnAtom]; + } + } + } + } + } + } + } + } + + if ( _s_log ) { + for(AtomToAtom::iterator it = _followOnStarts.begin(); it != _followOnStarts.end(); ++it) + fprintf(stderr, "start %s -> %s\n", it->first->name(), it->second->name()); + + for(AtomToAtom::iterator it = _followOnNexts.begin(); it != _followOnNexts.end(); ++it) + fprintf(stderr, "next %s -> %s\n", it->first->name(), (it->second != NULL) ? it->second->name() : "null"); + } +} + +void Layout::buildOrdinalOverrideMap() +{ + // if no -order_file, then skip building override map + if ( ! _haveOrderFile ) + return; + + // build fast name->atom table + this->buildNameTable(); + + // handle .o files that cannot have their atoms rearranged + // with the start/next maps of follow-on atoms we can process the order file and produce override ordinals + uint32_t index = 0; + uint32_t matchCount = 0; + for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) { + const ld::Atom* atom = this->findAtom(*it); + if ( atom != NULL ) { + AtomToAtom::iterator start = _followOnStarts.find(atom); + if ( start != _followOnStarts.end() ) { + // this symbol for the order file corresponds to an atom that is in a cluster that must lay out together + for(const ld::Atom* nextAtom = start->second; nextAtom != NULL; nextAtom = _followOnNexts[nextAtom]) { + AtomToOrdinal::iterator pos = _ordinalOverrideMap.find(nextAtom); + if ( pos == _ordinalOverrideMap.end() ) { + _ordinalOverrideMap[nextAtom] = index++; + if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->name(), nextAtom->file()->path()); + } + else { + if (_s_log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", + atom->name(), index, _followOnStarts[atom]->name(), _ordinalOverrideMap[atom] ); + } + } + } + else { + _ordinalOverrideMap[atom] = index; + if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->name(), atom->file()->path()); + } + ++matchCount; + } + else { + if ( _options.printOrderFileStatistics() ) { + if ( it->objectFileName == NULL ) + warning("can't find match for order_file entry: %s", it->symbolName); + else + warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName); + } + } + ++index; + } + if ( _options.printOrderFileStatistics() && (_options.orderedSymbolsCount() != matchCount) ) { + warning("only %u out of %lu order_file symbols were applicable", matchCount, _options.orderedSymbolsCount() ); + } + + +} + +void Layout::doPass() +{ + // handle .o files that cannot have their atoms rearranged + this->buildFollowOnTables(); + + // + this->buildOrdinalOverrideMap(); + + + // sort atoms in each section + for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( orderableSection(sect) ) { + std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); + } + else { + // bubble sort any typeSectionStart atom to the beginning + bool moving = false; + for (int i=sect->atoms.size()-1; i >= 0; --i) { + if ( moving ) { + const ld::Atom* temp = sect->atoms[i]; + sect->atoms[i] = sect->atoms[i+1]; + sect->atoms[i+1] = temp; + } + if ( sect->atoms[i]->contentType() == ld::Atom::typeSectionStart ) + moving = true; + } + // bubble sort any typeSectionEnd atom to the end + moving = false; + for (unsigned int i=sect->atoms.size(); i < sect->atoms.size(); ++i) { + if ( moving ) { + const ld::Atom* temp = sect->atoms[i]; + sect->atoms[i] = sect->atoms[i-1]; + sect->atoms[i-1] = temp; + } + if ( sect->atoms[i]->contentType() == ld::Atom::typeSectionEnd ) + moving = true; + } + } + } + + //fprintf(stderr, "Sorted atoms:\n"); + //for (std::vector<ld::Internal::FinalSection*>::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + // ld::Internal::FinalSection* sect = *sit; + // for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + // const ld::Atom* atom = *ait; + // fprintf(stderr, "\t%s\t%s\n", sect->sectionName(), atom->name()); + // } + //} + +} + + +void doPass(const Options& opts, ld::Internal& state) +{ + Layout layout(opts, state); + layout.doPass(); +} + + +} // namespace order_file +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/order_file.h b/src/ld/passes/order_file.h new file mode 100644 index 0000000..7a2aa87 --- /dev/null +++ b/src/ld/passes/order_file.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __ORDER_FILE_H__ +#define __ORDER_FILE_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace order_file { + +// called by linker to re-arrange atoms to improve locality and performance +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace order_file +} // namespace passes +} // namespace ld + +#endif // __ORDER_FILE_H__ diff --git a/src/ld/passes/stubs/make_stubs.h b/src/ld/passes/stubs/make_stubs.h new file mode 100644 index 0000000..7330bee --- /dev/null +++ b/src/ld/passes/stubs/make_stubs.h @@ -0,0 +1,39 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace passes { +namespace stubs { + +// called by linker to process atoms in Internal and add stubs as needed +extern void doPass(const Options& opts, ld::Internal& internal); + +} // namespace stubs +} // namespace passes +} // namespace ld + diff --git a/src/ld/passes/stubs/stub_arm.hpp b/src/ld/passes/stubs/stub_arm.hpp new file mode 100644 index 0000000..e8dedda --- /dev/null +++ b/src/ld/passes/stubs/stub_arm.hpp @@ -0,0 +1,416 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// already in ld::passes::stubs namespace +namespace arm { + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "non-lazy pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + + +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(28, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, compressedImageCache(pass)), + _fixup2(28, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(28, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 16), + _fixup4(28, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32), + _fixup5(32, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, compressedFastBinder(pass)), + _fixup6(32, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup7(32, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 28), + _fixup8(32, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return " stub helpers"; } + virtual uint64_t size() const { return 36; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + // push lazy-info-offset + OSWriteLittleInt32(&buffer[ 0], 0, 0xe52dc004); // str ip, [sp, #-4]! + // push address of dyld_mageLoaderCache + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59fc010); // ldr ip, L1 + OSWriteLittleInt32(&buffer[ 8], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[12], 0, 0xe52dc004); // str ip, [sp, #-4]! + // jump through _fast_lazy_bind + OSWriteLittleInt32(&buffer[16], 0, 0xe59fc008); // ldr ip, L2 + OSWriteLittleInt32(&buffer[20], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[24], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[28], 0, 0x00000000); // L1: .long fFastStubGOTAtom - (helperhelper+16) + OSWriteLittleInt32(&buffer[32], 0, 0x00000000); // L2: .long _fast_lazy_bind - (helperhelper+28) + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; + ld::Fixup _fixup6; + ld::Fixup _fixup7; + ld::Fixup _fixup8; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom* lazyPointer) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, helperHelper(pass)), + _fixup2(8, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup3(8, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[4], 0, 0xea000000); // b _helperhelper + OSWriteLittleInt32(&buffer[8], 0, 0); // .long lazy-info-offset + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom* lazyPointer) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + ld::Atom::symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1( 4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, &stubTo), + _fixup2(32, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, lazyPointer), + _fixup3(32, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup4(32, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 20), + _fixup5(32, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 36; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe92d400f); // push {r0, r1, r2, r3, lr} + OSWriteLittleInt32(&buffer[ 4], 0, 0xebfffffd); // bl _foo + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59fc010); // ldr ip, [pc, #16] + OSWriteLittleInt32(&buffer[12], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[16], 0, 0xe58c0000); // str r0, [ip] + OSWriteLittleInt32(&buffer[20], 0, 0xe1a0c000); // mov ip, r0 + OSWriteLittleInt32(&buffer[24], 0, 0xe8bd400f); // pop {r0, r1, r2, r3, lr} + OSWriteLittleInt32(&buffer[28], 0, 0xe12fff1c); // bx ip + OSWriteLittleInt32(&buffer[32], 0, 0x00000000); // .long foo$lazyptr - helper + 20 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup5)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, + bool weakImport, bool close) + : ld::Atom(close ? _s_sectionClose : _s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _helper(pass, stubTo, this), + _resolverHelper(pass, stubTo, this), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + stubToResolver ? &_resolverHelper : (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionClose; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionClose("__DATA", "__lazy_symbol", ld::Section::typeLazyPointerClose); + + + +class StubPICAtom : public ld::Atom { +public: + StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + + static ld::Section _s_section; +}; + +ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub4", ld::Section::typeStub); + + + +class StubNoPICAtom : public ld::Atom { +public: + StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _fixup(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub4", ld::Section::typeStub); + + + + +class StubCloseAtom : public ld::Atom { +public: + StubCloseAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, true), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMLoad12, &_lazyPointer) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xE59FF000); // ldr pc, [pc, #foo$lazy_ptr] + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubCloseAtom::_s_section("__TEXT", "__symbolstub1", ld::Section::typeStubClose); + + + + +} // namespace arm + diff --git a/src/ld/passes/stubs/stub_arm_classic.hpp b/src/ld/passes/stubs/stub_arm_classic.hpp new file mode 100644 index 0000000..50870f4 --- /dev/null +++ b/src/ld/passes/stubs/stub_arm_classic.hpp @@ -0,0 +1,155 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// already in ld::passes::stubs namespace +namespace arm { +namespace classic { + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(forLazyDylib ? _s_sectionLazy : _s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + +class StubPICAtom : public ld::Atom { +public: + StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc004); // ldr ip, pc + 12 + OSWriteLittleInt32(&buffer[ 4], 0, 0xe08fc00c); // add ip, pc, ip + OSWriteLittleInt32(&buffer[ 8], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[12], 0, 0x00000000); // .long L_foo$lazy_ptr - (L1$scv + 8) + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + + static ld::Section _s_section; +}; + +ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub4", ld::Section::typeStub); + + + +class StubNoPICAtom : public ld::Atom { +public: + StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, [pc, #0] + OSWriteLittleInt32(&buffer[ 4], 0, 0xe59cf000); // ldr pc, [ip] + OSWriteLittleInt32(&buffer[ 8], 0, 0x00000000); // .long L_foo$lazy_ptr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub4", ld::Section::typeStub); + + + + +} // namespace classic +} // namespace arm + diff --git a/src/ld/passes/stubs/stub_ppc_classic.hpp b/src/ld/passes/stubs/stub_ppc_classic.hpp new file mode 100644 index 0000000..6862b60 --- /dev/null +++ b/src/ld/passes/stubs/stub_ppc_classic.hpp @@ -0,0 +1,190 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace ppc { +namespace classic { + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, + forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, for64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, + for64 ? ld::Fixup::kindStoreTargetAddressBigEndian64 : ld::Fixup::kindStoreTargetAddressBigEndian32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo), + _for64(for64) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return _for64 ? 8 : 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + const bool _for64; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + + +class StubPICAtom : public ld::Atom { +public: + StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), + _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup4(12, ld::Fixup::k4of4, ld::Fixup::kindStorePPCPicHigh16AddLow), + _fixup5(20, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup6(20, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), + _fixup7(20, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 8), + _fixup8(20, ld::Fixup::k4of4, for64 ? ld::Fixup::kindStorePPCPicLow14 : ld::Fixup::kindStorePPCPicLow16), + _for64(for64) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 32; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteBigInt32(&buffer[ 0], 0, 0x7c0802a6); // mflr r0 + OSWriteBigInt32(&buffer[ 4], 0, 0x429f0005); // bcl 20,31,Lpicbase + OSWriteBigInt32(&buffer[ 8], 0, 0x7d6802a6); // Lpicbase: mflr r11 + OSWriteBigInt32(&buffer[12], 0, 0x3d6b0000); // addis r11,r11,ha16(L_fwrite$lazy_ptr-Lpicbase) + OSWriteBigInt32(&buffer[16], 0, 0x7c0803a6); // mtlr r0 + if ( _for64 ) + OSWriteBigInt32(&buffer[20], 0, 0xe98b0001);// ldu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + else + OSWriteBigInt32(&buffer[20], 0, 0x858b0000);// lwzu r12,lo16(L_fwrite$lazy_ptr-Lpicbase)(r11) + OSWriteBigInt32(&buffer[24], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[28], 0, 0x4e800420); // bctr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup8)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + mutable ld::Fixup _fixup4; + mutable ld::Fixup _fixup5; + mutable ld::Fixup _fixup6; + mutable ld::Fixup _fixup7; + mutable ld::Fixup _fixup8; + const bool _for64; + + static ld::Section _s_section; +}; + +ld::Section StubPICAtom::_s_section("__TEXT", "__picsymbolstub1", ld::Section::typeStub); + + + +class StubNoPICAtom : public ld::Atom { +public: + StubNoPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool for64, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, for64, weakImport), + _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStorePPCAbsHigh16AddLow), + _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, &_lazyPointer), + _fixup4(4, ld::Fixup::k2of2, for64 ? ld::Fixup::kindStorePPCAbsLow14 : ld::Fixup::kindStorePPCAbsLow16), + _for64(for64) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteBigInt32(&buffer[ 0], 0, 0x3d600000); // lis r11,ha16(L_foo$lazy_ptr) + if ( _for64 ) + OSWriteBigInt32(&buffer[ 4], 0, 0xe98b0001);// ldu r12,lo16(L_foo$lazy_ptr)(r11) + else + OSWriteBigInt32(&buffer[ 4], 0, 0x858b0000);// lwzu r12,lo16(L_foo$lazy_ptr)(r11) + OSWriteBigInt32(&buffer[ 8], 0, 0x7d8903a6); // mtctr r12 + OSWriteBigInt32(&buffer[12], 0, 0x4e800420); // bctr + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + mutable ld::Fixup _fixup4; + const bool _for64; + + static ld::Section _s_section; +}; + +ld::Section StubNoPICAtom::_s_section("__TEXT", "__symbol_stub1", ld::Section::typeStub); + + +} // namespace classic +} // namespace ppc + diff --git a/src/ld/passes/stubs/stub_x86.hpp b/src/ld/passes/stubs/stub_x86.hpp new file mode 100644 index 0000000..0eda4ad --- /dev/null +++ b/src/ld/passes/stubs/stub_x86.hpp @@ -0,0 +1,347 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace x86 { + + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "image cache pointer"; } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + + + + +// +// The stub-helper-helper is the common code factored out of each helper function. +// It is in the same section as the stub-helpers. +// Similar to the PLT0 entry in ELF. +// +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(1, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, compressedImageCache(pass)), + _fixup2(7, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, compressedFastBinder(pass)) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return "helper helper"; } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushl $dyld_ImageLoaderCache + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xFF; // jmp *_fast_lazy_bind + buffer[6] = 0x25; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x90; // nop + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(1, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup2(1, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32), + _fixup3(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, helperHelper(pass)) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushl $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp helperhelper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, &stubTo), + _fixup2(9, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, lazyPointer), + _fixup3(18, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, lazyPointer) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 22; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[ 0] = 0x50; // push %eax + buffer[ 1] = 0x51; // push %ecx + buffer[ 2] = 0x52; // push %edx + buffer[ 3] = 0xE8; // call foo + buffer[ 4] = 0x00; + buffer[ 5] = 0x00; + buffer[ 6] = 0x00; + buffer[ 7] = 0x00; + buffer[ 8] = 0xA3; // movl %eax,foo$lazy_ptr + buffer[ 9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x5A; // pop %edx + buffer[14] = 0x59; // pop %ecx + buffer[15] = 0x58; // pop %eax + buffer[16] = 0xFF; // jmp *foo$lazy_ptr + buffer[17] = 0x25; + buffer[18] = 0x00; + buffer[19] = 0x00; + buffer[20] = 0x00; + buffer[21] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _helper(pass, this, stubTo), + _resolverHelper(pass, this, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, + stubToResolver ? &_resolverHelper : (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__symbol_stub", ld::Section::typeStub); + + + +} // namespace x86 + diff --git a/src/ld/passes/stubs/stub_x86_64.hpp b/src/ld/passes/stubs/stub_x86_64.hpp new file mode 100644 index 0000000..e74b37d --- /dev/null +++ b/src/ld/passes/stubs/stub_x86_64.hpp @@ -0,0 +1,366 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace x86_64 { + + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "image cache pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); + + + + + +// +// The stub-helper-helper is the common code factored out of each helper function. +// It is in the same section as the stub-helpers. +// Similar to the PLT0 entry in ELF. +// +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(3, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, compressedImageCache(pass)), + _fixup2(11, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, compressedFastBinder(pass)) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return "helper helper"; } + virtual uint64_t size() const { return 16; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x4C; // leaq dyld_mageLoaderCache(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x41; // pushq %r11 + buffer[8] = 0x53; + buffer[9] = 0xFF; // jmp *_fast_lazy_bind(%rip) + buffer[10] = 0x25; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x00; + buffer[15] = 0x90; // nop + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(1, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup2(1, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32), + _fixup3(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, helperHelper(pass)) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushq $lazy-info-offset + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp helperhelper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(10, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, &stubTo), + _fixup2(17, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, lazyPointer), + _fixup3(32, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, lazyPointer) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** nm) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 36; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[ 0] = 0x50; // push %rax + buffer[ 1] = 0x57; // push %rdi + buffer[ 2] = 0x56; // push %rsi + buffer[ 3] = 0x52; // push %rdx + buffer[ 4] = 0x51; // push %rcx + buffer[ 5] = 0x41; // push %r8 + buffer[ 6] = 0x50; + buffer[ 7] = 0x41; // push %r9 + buffer[ 8] = 0x51; + buffer[ 9] = 0xE8; // call foo + buffer[10] = 0x00; + buffer[11] = 0x00; + buffer[12] = 0x00; + buffer[13] = 0x00; + buffer[14] = 0x48; // movq %rax,foo$lazy_pointer(%rip) + buffer[15] = 0x89; + buffer[16] = 0x05; + buffer[17] = 0x00; + buffer[18] = 0x00; + buffer[19] = 0x00; + buffer[20] = 0x00; + buffer[21] = 0x41; // pop %r9 + buffer[22] = 0x59; + buffer[23] = 0x41; // pop %r8 + buffer[24] = 0x58; + buffer[25] = 0x59; // pop %rcx + buffer[26] = 0x5A; // pop %rdx + buffer[27] = 0x5E; // pop %rsi + buffer[28] = 0x5F; // pop %rdi + buffer[29] = 0x58; // pop %rax + buffer[30] = 0xFF; // jmp *foo$lazy_ptr(%rip) + buffer[31] = 0x25; + buffer[32] = 0x00; + buffer[33] = 0x00; + buffer[34] = 0x00; + buffer[35] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _helper(pass, this, stubTo), + _resolverHelper(pass, this, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + stubToResolver ? &_resolverHelper : + (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + + +} // namespace x86_64 + diff --git a/src/ld/passes/stubs/stub_x86_64_classic.hpp b/src/ld/passes/stubs/stub_x86_64_classic.hpp new file mode 100644 index 0000000..14507a2 --- /dev/null +++ b/src/ld/passes/stubs/stub_x86_64_classic.hpp @@ -0,0 +1,165 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace x86_64 { +namespace classic { + + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom& lazyPointer, bool forLazyDylib) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(3, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &lazyPointer), + _fixup2(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x4C; // lea foo$lazy_ptr(%rip),%r11 + buffer[1] = 0x8D; + buffer[2] = 0x1D; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0xE9; // jmp dyld_stub_binding_helper + buffer[8] = 0x00; + buffer[9] = 0x00; + buffer[10] = 0x00; + buffer[11] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, + forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _helper(pass, stubTo, *this, forLazyDylib), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &_helper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86PCRel32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + +} // namespace classic +} // namespace x86_64 + diff --git a/src/ld/passes/stubs/stub_x86_classic.hpp b/src/ld/passes/stubs/stub_x86_classic.hpp new file mode 100644 index 0000000..0562218 --- /dev/null +++ b/src/ld/passes/stubs/stub_x86_classic.hpp @@ -0,0 +1,163 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace x86 { +namespace classic { + + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + const ld::Atom& lazyPointer, bool forLazyDylib) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _fixup1(1, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &lazyPointer), + _fixup2(6, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressX86BranchPCRel32, + forLazyDylib ? pass.internal()->lazyBindingHelper : pass.internal()->classicBindingHelper) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0x68; // pushl $foo$lazy_ptr + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0xE9; // jmp dyld_stub_binding_helper + buffer[6] = 0x00; + buffer[7] = 0x00; + buffer[8] = 0x00; + buffer[9] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom( forLazyDylib ? _s_sectionLazy : _s_section, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, + forLazyDylib ? ld::Atom::typeLazyDylibPointer : ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _stubTo(stubTo), + _helper(pass, stubTo, *this, forLazyDylib), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_helper), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) + { _fixup2.weakImport = weakImport; pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 4; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; + static ld::Section _s_sectionLazy; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionLazy("__DATA", "__ld_symbol_ptr", ld::Section::typeLazyDylibPointer); + + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool forLazyDylib, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, forLazyDylib, weakImport), + _fixup(2, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual bool translationUnitSource(const char** dir, const char** ) const + { return false; } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 6; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + buffer[0] = 0xFF; // jmp *foo$lazy_pointer(%rip) + buffer[1] = 0x25; + buffer[2] = 0x00; + buffer[3] = 0x00; + buffer[4] = 0x00; + buffer[5] = 0x00; + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__symbol_stub", ld::Section::typeStub); + + +} // namespace classic +} // namespace x86 + diff --git a/src/ld/passes/stubs/stubs.cpp b/src/ld/passes/stubs/stubs.cpp new file mode 100644 index 0000000..0e23c27 --- /dev/null +++ b/src/ld/passes/stubs/stubs.cpp @@ -0,0 +1,385 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <assert.h> +#include <libkern/OSByteOrder.h> + +#include <vector> +#include <set> +#include <map> + +#include "Options.h" +#include "ld.hpp" + +#include "make_stubs.h" + + +namespace ld { +namespace passes { +namespace stubs { + +class Pass { +public: + Pass(const Options& opts); + void process(ld::Internal& internal); + void addAtom(const ld::Atom& atom) { _internal->addAtom(atom); } + bool usingCompressedLINKEDIT() const { return _compressedLINKEDIT; } + ld::Internal* internal() { return _internal; } + + Atom* compressedHelperHelper; + Atom* compressedImageCache; + Atom* compressedFastBinderPointer; + +private: + + struct AtomByNameSorter + { + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } + }; + + const ld::Atom* stubableFixup(const ld::Fixup* fixup, ld::Internal&); + ld::Atom* makeStub(const ld::Atom& target, bool weakImport); + void verifyNoResolverFunctions(ld::Internal& state); + + const Options& _options; + const cpu_type_t _architecture; + const bool _lazyDylibsInUuse; + const bool _compressedLINKEDIT; + const bool _prebind; + const bool _mightBeInSharedRegion; + const bool _pic; + const bool _flatNamespace; + ld::Internal* _internal; + uint32_t _stubCount; + bool _largeText; +}; + +#include "stub_x86_64.hpp" +#include "stub_x86_64_classic.hpp" +#include "stub_x86.hpp" +#include "stub_x86_classic.hpp" +#include "stub_arm.hpp" +#include "stub_arm_classic.hpp" +#include "stub_ppc_classic.hpp" + + + +Pass::Pass(const Options& opts) + : compressedHelperHelper(NULL), + compressedImageCache(NULL), + compressedFastBinderPointer(NULL), + _options(opts), + _architecture(opts.architecture()), + _lazyDylibsInUuse(opts.usingLazyDylibLinking()), + _compressedLINKEDIT(opts.makeCompressedDyldInfo()), + _prebind(opts.prebind()), + _mightBeInSharedRegion(opts.sharedRegionEligible()), + _pic(opts.outputSlidable()), + _flatNamespace(opts.nameSpace() != Options::kTwoLevelNameSpace), + _internal(NULL), _stubCount(0), _largeText(false) +{ + +} + + +const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) +{ + if ( fixup->binding == ld::Fixup::bindingsIndirectlyBound ) { + const ld::Atom* target = state.indirectBindingTable[fixup->u.bindingIndex]; + switch ( fixup->kind ) { + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + // create stub if target is in a dylib + if ( target->definition() == ld::Atom::definitionProxy ) + return target; + // use stub if target is a resolver function in same linkage unit + if ( target->contentType() == ld::Atom::typeResolver ) + return target; + if ( target->scope() == ld::Atom::scopeGlobal ) { + // create stub if target is global weak definition in symbol table + if ( (target->definition() == ld::Atom::definitionRegular) + && (target->combine() == ld::Atom::combineByName) + && ((target->symbolTableInclusion() == ld::Atom::symbolTableIn) + || (target->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) + return target; + // create stub if target is interposable + if ( _options.interposable(target->name()) ) + return target; + if ( _flatNamespace ) { + // flat namespace does not indirect calls within main exectuables + if ( _options.outputKind() == Options::kDynamicExecutable ) + return NULL; + // create stub if target is global and building -flat dylib or bundle + return target; + } + } + break; + default: + if ( target->contentType() == ld::Atom::typeResolver ) { + // any pointer to a resolver needs to change to pointer to stub + return target; + } + break; + } + } + return NULL; +} + + + +ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) +{ + //fprintf(stderr, "makeStub(target=%p %s in sect %s)\n", &target, target.name(), target.section().sectionName()); + bool stubToGlobalWeakDef = ( (target.scope() == ld::Atom::scopeGlobal) + && (target.definition() == ld::Atom::definitionRegular) + && (target.combine() == ld::Atom::combineByName) ); + + bool forLazyDylib = false; + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target.file()); + if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) + forLazyDylib = true; + bool stubToResolver = (target.contentType() == ld::Atom::typeResolver); + + switch ( _architecture ) { + case CPU_TYPE_POWERPC: + if ( _pic ) + return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, false, weakImport); + else + return new ld::passes::stubs::ppc::classic::StubNoPICAtom(*this, target, forLazyDylib, false, weakImport); + break; + case CPU_TYPE_POWERPC64: + return new ld::passes::stubs::ppc::classic::StubPICAtom(*this, target, forLazyDylib, true, weakImport); + break; + case CPU_TYPE_I386: + if ( usingCompressedLINKEDIT() && !forLazyDylib ) + return new ld::passes::stubs::x86::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else + return new ld::passes::stubs::x86::classic::StubAtom(*this, target, forLazyDylib, weakImport); + break; + case CPU_TYPE_X86_64: + if ( usingCompressedLINKEDIT() && !forLazyDylib ) + return new ld::passes::stubs::x86_64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else + return new ld::passes::stubs::x86_64::classic::StubAtom(*this, target, forLazyDylib, weakImport); + break; + case CPU_TYPE_ARM: + if ( usingCompressedLINKEDIT() && !forLazyDylib ) { + if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText ) + return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else if ( _pic ) + return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + else + return new ld::passes::stubs::arm::StubNoPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + } + else { + if ( _pic ) + return new ld::passes::stubs::arm::classic::StubPICAtom(*this, target, forLazyDylib, weakImport); + else + return new ld::passes::stubs::arm::classic::StubNoPICAtom(*this, target, forLazyDylib, weakImport); + } + break; + } + throw "unsupported arch for stub"; +} + + +void Pass::verifyNoResolverFunctions(ld::Internal& state) +{ + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + if ( atom->contentType() == ld::Atom::typeResolver ) + throwf("resolver function '%s' not supported in type of output", atom->name()); + } + } +} + +void Pass::process(ld::Internal& state) +{ + switch ( _options.outputKind() ) { + case Options::kObjectFile: + // these kinds don't use stubs and can have resolver functions + return; + case Options::kKextBundle: + case Options::kStaticExecutable: + case Options::kPreload: + case Options::kDyld: + // these kinds don't use stubs and cannot have resolver functions + verifyNoResolverFunctions(state); + return; + case Options::kDynamicLibrary: + // uses stubs and can have resolver functions + break; + case Options::kDynamicExecutable: + case Options::kDynamicBundle: + // these kinds do use stubs and cannot have resolver functions + verifyNoResolverFunctions(state); + break; + } + + // walk all atoms and fixups looking for stubable references + // don't create stubs inline because that could invalidate the sections iterator + std::vector<const ld::Atom*> atomsCallingStubs; + std::map<const ld::Atom*,ld::Atom*> stubFor; + std::map<const ld::Atom*,bool> weakImportMap; + atomsCallingStubs.reserve(128); + uint64_t codeSize = 0; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + codeSize += atom->size(); + bool atomNeedsStub = false; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + const ld::Atom* stubableTargetOfFixup = stubableFixup(fit, state); + if ( stubableTargetOfFixup != NULL ) { + if ( !atomNeedsStub ) { + atomsCallingStubs.push_back(atom); + atomNeedsStub = true; + } + stubFor[stubableTargetOfFixup] = NULL; + // record weak_import attribute + std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(stubableTargetOfFixup); + if ( pos == weakImportMap.end() ) { + // target not in weakImportMap, so add + weakImportMap[stubableTargetOfFixup] = fit->weakImport; + // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(stubableTargetOfFixup->file()); + if ( dylib != NULL ) { + if ( fit->weakImport ) + (const_cast<ld::dylib::File*>(dylib))->setUsingWeakImportedSymbols(); + else + (const_cast<ld::dylib::File*>(dylib))->setUsingNonWeakImportedSymbols(); + } + } + else { + // target in weakImportMap, check for weakness mismatch + if ( pos->second != fit->weakImport ) { + // found mismatch + switch ( _options.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", stubableTargetOfFixup->name()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + } + } + // all resolver functions must have a corresponding stub + if ( atom->contentType() == ld::Atom::typeResolver ) { + if ( _options.outputKind() != Options::kDynamicLibrary ) + throwf("resolver functions (%s) can only be used in dylibs", atom->name()); + if ( !_options.makeCompressedDyldInfo() ) { + if ( _options.architecture() == CPU_TYPE_POWERPC ) + throwf("resolver functions (%s) not supported for PowerPC", atom->name()); + else if ( _options.architecture() == CPU_TYPE_ARM ) + throwf("resolver functions (%s) can only be used when targeting iOS 4.2 or later", atom->name()); + else + throwf("resolver functions (%s) can only be used when targeting Mac OS X 10.6 or later", atom->name()); + } + stubFor[atom] = NULL; + } + } + } + + // short circuit if no stubs needed + _internal = &state; + _stubCount = stubFor.size(); + if ( _stubCount == 0 ) + return; + + // <rdar://problem/8553283> lazily check for helper + if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) ) + throw "symbol dyld_stub_binding_helper not found, normally in crt1.o/dylib1.o/bundle1.o"; + + // disable close stubs when branch islands might be needed + if ( (_architecture == CPU_TYPE_ARM) && (codeSize > 4*1024*1024) ) + _largeText = true; + + // make stub atoms + for (std::map<const ld::Atom*,ld::Atom*>::iterator it = stubFor.begin(); it != stubFor.end(); ++it) { + it->second = makeStub(*it->first, weakImportMap[it->first]); + } + + // updated atoms to use stubs + for (std::vector<const ld::Atom*>::iterator it=atomsCallingStubs.begin(); it != atomsCallingStubs.end(); ++it) { + const ld::Atom* atom = *it; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + const ld::Atom* stubableTargetOfFixup = stubableFixup(fit, state); + if ( stubableTargetOfFixup != NULL ) { + ld::Atom* stub = stubFor[stubableTargetOfFixup]; + assert(stub != NULL && "stub not created"); + fit->binding = ld::Fixup::bindingDirectlyBound; + fit->u.target = stub; + } + } + } + + // sort new atoms so links are consistent + for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + switch ( sect->type() ) { + case ld::Section::typeStubHelper: + case ld::Section::typeStub: + case ld::Section::typeStubClose: + case ld::Section::typeLazyPointer: + case ld::Section::typeLazyPointerClose: + std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter()); + break; + default: + break; + } + } + +} + + +void doPass(const Options& opts, ld::Internal& internal) +{ + Pass pass(opts); + pass.process(internal); +} + + + +} // namespace stubs +} // namespace passes +} // namespace ld + diff --git a/src/ld/passes/tlvp.cpp b/src/ld/passes/tlvp.cpp new file mode 100644 index 0000000..f40d018 --- /dev/null +++ b/src/ld/passes/tlvp.cpp @@ -0,0 +1,303 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include <stdint.h> +#include <math.h> +#include <unistd.h> +#include <dlfcn.h> + +#include <vector> +#include <map> +#include <ext/hash_map> + +#include "ld.hpp" +#include "tlvp.h" + +namespace ld { +namespace passes { +namespace tlvp { + +class File; // forward reference + +class TLVEntryAtom : public ld::Atom { +public: + TLVEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), + _target(target) + { _fixup.weakImport = weakImport; internal.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual bool translationUnitSource(const char** dir, const char**) const + { return false; } + virtual const char* name() const { return _target->name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + const ld::Atom* _target; + + static ld::Section _s_section; +}; + +ld::Section TLVEntryAtom::_s_section("__DATA", "__thread_ptrs", ld::Section::typeTLVPointers); + + +static bool optimizable(const Options& opts, const ld::Atom* targetOfTLV) +{ + // cannot do LEA optimization if target is in another dylib + if ( targetOfTLV->definition() == ld::Atom::definitionProxy ) + return false; + if ( targetOfTLV->scope() == ld::Atom::scopeGlobal ) { + // cannot do LEA optimization if target is weak exported symbol + if ( (targetOfTLV->definition() == ld::Atom::definitionRegular) && (targetOfTLV->combine() == ld::Atom::combineByName) ) + return false; + // cannot do LEA optimization if target is interposable + if ( opts.interposable(targetOfTLV->name()) ) + return false; + // cannot do LEA optimization for flat-namespace + if ( opts.nameSpace() != Options::kTwoLevelNameSpace ) + return false; + } + return true; +} + +struct AtomByNameSorter +{ + bool operator()(const ld::Atom* left, const ld::Atom* right) + { + return (strcmp(left->name(), right->name()) < 0); + } +}; + +struct TlVReferenceCluster +{ + const ld::Atom* targetOfTLV; + ld::Fixup* fixupWithTarget; + ld::Fixup* fixupWithTLVStore; + bool optimizable; +}; + +void doPass(const Options& opts, ld::Internal& internal) +{ + const bool log = false; + + // only make tlv section in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // walk all atoms and fixups looking for TLV references and add them to list + std::vector<TlVReferenceCluster> references; + for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + TlVReferenceCluster ref; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + ref.targetOfTLV = NULL; + ref.fixupWithTarget = NULL; + ref.fixupWithTLVStore = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingsIndirectlyBound: + ref.targetOfTLV = internal.indirectBindingTable[fit->u.bindingIndex]; + ref.fixupWithTarget = fit; + break; + case ld::Fixup::bindingDirectlyBound: + ref.targetOfTLV = fit->u.target; + ref.fixupWithTarget = fit; + break; + default: + break; + } + switch ( fit->kind ) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86Abs32TLVLoad: + ref.fixupWithTLVStore = fit; + break; + default: + break; + } + if ( fit->lastInCluster() && (ref.fixupWithTLVStore != NULL) ) { + ref.optimizable = optimizable(opts, ref.targetOfTLV); + if (log) fprintf(stderr, "found reference to TLV at %s+0x%X to %s\n", + atom->name(), ref.fixupWithTLVStore->offsetInAtom, ref.targetOfTLV->name()); + if ( ! opts.canUseThreadLocalVariables() ) { + throwf("targeted OS version does not support use of thread local variables in %s", atom->name()); + } + references.push_back(ref); + } + } + } + } + + // compute which TLV references will be weak_imports + std::map<const ld::Atom*,bool> weakImportMap; + for(std::vector<TlVReferenceCluster>::iterator it=references.begin(); it != references.end(); ++it) { + if ( !it->optimizable ) { + // record weak_import attribute + std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(it->targetOfTLV); + if ( pos == weakImportMap.end() ) { + // target not in weakImportMap, so add + weakImportMap[it->targetOfTLV] = it->fixupWithTarget->weakImport; + // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB + const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(it->targetOfTLV->file()); + if ( dylib != NULL ) { + if ( it->fixupWithTarget->weakImport ) + (const_cast<ld::dylib::File*>(dylib))->setUsingWeakImportedSymbols(); + else + (const_cast<ld::dylib::File*>(dylib))->setUsingNonWeakImportedSymbols(); + } + } + else { + // target in weakImportMap, check for weakness mismatch + if ( pos->second != it->fixupWithTarget->weakImport ) { + // found mismatch + switch ( opts.weakReferenceMismatchTreatment() ) { + case Options::kWeakReferenceMismatchError: + throwf("mismatching weak references for symbol: %s", it->targetOfTLV->name()); + case Options::kWeakReferenceMismatchWeak: + pos->second = true; + break; + case Options::kWeakReferenceMismatchNonWeak: + pos->second = false; + break; + } + } + } + } + } + + // create TLV pointers for TLV references that cannot be optimized + std::map<const ld::Atom*,ld::Atom*> variableToPointerMap; + for(std::map<const ld::Atom*,bool>::iterator it=weakImportMap.begin(); it != weakImportMap.end(); ++it) { + std::map<const ld::Atom*,ld::Atom*>::iterator pos = variableToPointerMap.find(it->first); + if ( pos == variableToPointerMap.end() ) { + if (log) fprintf(stderr, "make TLV pointer for %s\n", it->first->name()); + if ( it->first->contentType() != ld::Atom::typeTLV ) + throwf("illegal thread local variable reference to regular symbol %s", it->first->name()); + TLVEntryAtom* tlvp = new TLVEntryAtom(internal, it->first, it->second); + variableToPointerMap[it->first] = tlvp; + } + } + + // sort new tvlp atoms so links are consistent + for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeTLVPointers ) { + std::sort(sect->atoms.begin(), sect->atoms.end(), AtomByNameSorter()); + } + } + + // update references to use TLV pointers or TLV object directly + for(std::vector<TlVReferenceCluster>::iterator it=references.begin(); it != references.end(); ++it) { + if ( it->optimizable ) { + // change store to be LEA instead load (mov) + if (log) fprintf(stderr, "optimizing load of TLV to %s into an LEA\n", it->targetOfTLV->name()); + it->fixupWithTLVStore->binding = ld::Fixup::bindingDirectlyBound; + it->fixupWithTLVStore->u.target = it->targetOfTLV; + switch ( it->fixupWithTLVStore->kind ) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA; + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA; + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA; + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA; + break; + default: + assert(0 && "bad store kind for TLV optimization"); + } + } + else { + // change target to be new TLV pointer atom + if (log) fprintf(stderr, "updating load of TLV to %s to load from TLV pointer\n", it->targetOfTLV->name()); + const ld::Atom* tlvpAtom = variableToPointerMap[it->targetOfTLV]; + assert(tlvpAtom != NULL); + it->fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + it->fixupWithTarget->u.target = tlvpAtom; + } + } + + + + // alter tlv definitions to have an offset as third field + for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeTLVDefs ) + continue; + for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->offsetInAtom != 0 ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound && "thread variable def contains pointer to global"); + switch( fit->u.target->contentType() ) { + case ld::Atom::typeTLVZeroFill: + case ld::Atom::typeTLVInitialValue: + switch ( fit->kind ) { + case ld::Fixup::kindSetTargetAddress: + fit->kind = ld::Fixup::kindSetTargetTLVTemplateOffset; + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + fit->kind = ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32; + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + fit->kind = ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64; + break; + default: + assert(0 && "bad kind for target in tlv defs"); + break; + } + break; + default: + assert(0 && "wrong content type for target in tlv defs"); + break; + } + } + } + } + } + +} + + + +} // namespace tlvp +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/tlvp.h b/src/ld/passes/tlvp.h new file mode 100644 index 0000000..0bb8786 --- /dev/null +++ b/src/ld/passes/tlvp.h @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __TLVP_H__ +#define __TLVP_H__ + +#include "Options.h" +#include "ld.hpp" + + +namespace ld { +namespace passes { +namespace tlvp { + +// called by linker to create TLVP entries and optimize TLV loads into LEAs instead +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace tlvp +} // namespace passes +} // namespace ld + +#endif // __TLVP_H__ diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index 25352b1..abf7162 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,28 +26,30 @@ #include <sys/stat.h> #include <sys/mman.h> #include <fcntl.h> -#include <fcntl.h> - -#include "MachOReaderRelocatable.hpp" +#include <mach-o/nlist.h> +#include <mach-o/stab.h> +#include <mach-o/fat.h> +#include <mach-o/loader.h> -#define LTO_SUPPORT 1 - -#if LTO_SUPPORT - #include "LTOReader.hpp" -#endif +#include "MachOFileAbstraction.hpp" +#include "parsers/macho_relocatable_file.h" +#include "parsers/lto_file.h" static bool sDumpContent= true; static bool sDumpStabs = false; static bool sSort = true; static bool sNMmode = false; +static bool sShowSection = true; +static bool sShowDefinitionKind = true; +static bool sShowCombineKind = true; + static cpu_type_t sPreferredArch = CPU_TYPE_I386; static cpu_subtype_t sPreferredSubArch = 0xFFFFFFFF; -static const char* sMatchName; +static const char* sMatchName = NULL; static int sPrintRestrict; static int sPrintAlign; static int sPrintName; - __attribute__((noreturn)) void throwf(const char* format, ...) { @@ -71,12 +73,12 @@ void warning(const char* format, ...) fprintf(stderr, "\n"); } -static void dumpStabs(std::vector<ObjectFile::Reader::Stab>* stabs) +static void dumpStabs(const std::vector<ld::relocatable::File::Stab>* stabs) { // debug info printf("stabs: (%lu)\n", stabs->size()); - for (std::vector<ObjectFile::Reader::Stab>::iterator it = stabs->begin(); it != stabs->end(); ++it ) { - ObjectFile::Reader::Stab& stab = *it; + for (std::vector<ld::relocatable::File::Stab>::const_iterator it = stabs->begin(); it != stabs->end(); ++it ) { + const ld::relocatable::File::Stab& stab = *it; const char* code = "?????"; switch (stab.type) { case N_GSYM: @@ -164,24 +166,24 @@ static void dumpStabs(std::vector<ObjectFile::Reader::Stab>* stabs) code = "LENG"; break; } - printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->getDisplayName() : ""), stab.other, stab.desc, code, stab.string); + printf(" [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->name() : ""), stab.other, stab.desc, code, stab.string); } } - -static void dumpAtomLikeNM(ObjectFile::Atom* atom) +#if 0 +static void dumpAtomLikeNM(ld::Atom* atom) { - uint32_t size = atom->getSize(); + uint32_t size = atom->size(); const char* visibility; - switch ( atom->getScope() ) { - case ObjectFile::Atom::scopeTranslationUnit: + switch ( atom->scope() ) { + case ld::Atom::scopeTranslationUnit: visibility = "internal"; break; - case ObjectFile::Atom::scopeLinkageUnit: + case ld::Atom::scopeLinkageUnit: visibility = "hidden "; break; - case ObjectFile::Atom::scopeGlobal: + case ld::Atom::scopeGlobal: visibility = "global "; break; default: @@ -190,17 +192,17 @@ static void dumpAtomLikeNM(ObjectFile::Atom* atom) } const char* kind; - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: + switch ( atom->definitionKind() ) { + case ld::Atom::kRegularDefinition: kind = "regular "; break; - case ObjectFile::Atom::kTentativeDefinition: + case ld::Atom::kTentativeDefinition: kind = "tentative"; break; - case ObjectFile::Atom::kWeakDefinition: + case ld::Atom::kWeakDefinition: kind = "weak "; break; - case ObjectFile::Atom::kAbsoluteSymbol: + case ld::Atom::kAbsoluteSymbol: kind = "absolute "; break; default: @@ -208,31 +210,31 @@ static void dumpAtomLikeNM(ObjectFile::Atom* atom) break; } - printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName()); + printf("0x%08X %s %s %s\n", size, visibility, kind, atom->name()); } -static void dumpAtom(ObjectFile::Atom* atom) +static void dumpAtom(ld::Atom* atom) { - if(sMatchName && strcmp(sMatchName, atom->getDisplayName())) + if(sMatchName && strcmp(sMatchName, atom->name())) return; //printf("atom: %p\n", atom); // name if(!sPrintRestrict || sPrintName) - printf("name: %s\n", atom->getDisplayName()); + printf("name: %s\n", atom->name()); // scope if(!sPrintRestrict) - switch ( atom->getScope() ) { - case ObjectFile::Atom::scopeTranslationUnit: + switch ( atom->scope() ) { + case ld::Atom::scopeTranslationUnit: printf("scope: translation unit\n"); break; - case ObjectFile::Atom::scopeLinkageUnit: + case ld::Atom::scopeLinkageUnit: printf("scope: linkage unit\n"); break; - case ObjectFile::Atom::scopeGlobal: + case ld::Atom::scopeGlobal: printf("scope: global\n"); break; default: @@ -241,23 +243,23 @@ static void dumpAtom(ObjectFile::Atom* atom) // kind if(!sPrintRestrict) - switch ( atom->getDefinitionKind() ) { - case ObjectFile::Atom::kRegularDefinition: + switch ( atom->definitionKind() ) { + case ld::Atom::kRegularDefinition: printf("kind: regular\n"); break; - case ObjectFile::Atom::kWeakDefinition: + case ld::Atom::kWeakDefinition: printf("kind: weak\n"); break; - case ObjectFile::Atom::kTentativeDefinition: + case ld::Atom::kTentativeDefinition: printf("kind: tentative\n"); break; - case ObjectFile::Atom::kExternalDefinition: + case ld::Atom::kExternalDefinition: printf("kind: import\n"); break; - case ObjectFile::Atom::kExternalWeakDefinition: + case ld::Atom::kExternalWeakDefinition: printf("kind: weak import\n"); break; - case ObjectFile::Atom::kAbsoluteSymbol: + case ld::Atom::kAbsoluteSymbol: printf("kind: absolute symbol\n"); break; default: @@ -265,16 +267,14 @@ static void dumpAtom(ObjectFile::Atom* atom) } // segment and section - if(!sPrintRestrict && (atom->getSectionName() != NULL) ) - printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName()); + if(!sPrintRestrict && (atom->section().sectionName() != NULL) ) + printf("section: %s,%s\n", atom->section().segmentName(), atom->section().sectionName()); // attributes if(!sPrintRestrict) { printf("attrs: "); if ( atom->dontDeadStrip() ) printf("dont-dead-strip "); - if ( atom->isZeroFill() ) - printf("zero-fill "); if ( atom->isThumb() ) printf("thumb "); printf("\n"); @@ -282,20 +282,20 @@ static void dumpAtom(ObjectFile::Atom* atom) // size if(!sPrintRestrict) - printf("size: 0x%012llX\n", atom->getSize()); + printf("size: 0x%012llX\n", atom->size()); // alignment if(!sPrintRestrict || sPrintAlign) - printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) ); + printf("align: %u mod %u\n", atom->alignment().modulus, (1 << atom->alignment().powerOf2) ); // content if (!sPrintRestrict && sDumpContent ) { - uint64_t size = atom->getSize(); + uint64_t size = atom->size(); if ( size < 4096 ) { uint8_t content[size]; atom->copyRawContent(content); printf("content: "); - if ( atom->getContentType() == ObjectFile::Atom::kCStringType ) { + if ( atom->contentType() == ld::Atom::typeCString ) { printf("\""); for (unsigned int i=0; i < size; ++i) { if(content[i]<'!' || content[i]>=127) @@ -317,12 +317,12 @@ static void dumpAtom(ObjectFile::Atom* atom) if(!sPrintRestrict) { if ( atom->beginUnwind() != atom->endUnwind() ) { printf("unwind encodings:\n"); - for (ObjectFile::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) { + for (ld::Atom::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) { printf("\t 0x%04X 0x%08X\n", it->startOffset, it->unwindInfo); } } } - +#if 0 // references if(!sPrintRestrict) { std::vector<ObjectFile::Reference*>& references = atom->getReferences(); @@ -333,13 +333,12 @@ static void dumpAtom(ObjectFile::Atom* atom) printf(" %s\n", ref->getDescription()); } } - +#endif // line info if(!sPrintRestrict) { - std::vector<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo(); - if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) { - printf("line info: (%lu)\n", lineInfo->size()); - for (std::vector<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) { + if ( atom->beginLineInfo() != atom->endLineInfo() ) { + printf("line info:\n"); + for (ld::Atom::LineInfo::iterator it = atom->beginLineInfo(); it != atom->endLineInfo(); ++it) { printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); } } @@ -348,31 +347,711 @@ static void dumpAtom(ObjectFile::Atom* atom) if(!sPrintRestrict) printf("\n"); } - +#endif struct AtomSorter { - bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right) + bool operator()(const ld::Atom* left, const ld::Atom* right) { if ( left == right ) return false; - int name = strcmp(left->getDisplayName(), right->getDisplayName()); - if ( name == 0 ) - return (left->getSize() < right->getSize()); - else - return ( name < 0); + // first sort by segment name + int diff = strcmp(left->section().segmentName(), right->section().segmentName()); + if ( diff != 0 ) + return (diff > 0); + + // then sort by section name + diff = strcmp(left->section().sectionName(), right->section().sectionName()); + if ( diff != 0 ) + return (diff < 0); + + // then sort by atom name + diff = strcmp(left->name(), right->name()); + if ( diff != 0 ) + return (diff < 0); + + // if cstring, sort by content + if ( left->contentType() == ld::Atom::typeCString ) { + diff = strcmp((char*)left->rawContentPointer(), (char*)right->rawContentPointer()); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeCStringPointer ) { + // if pointer to c-string sort by name + const char* leftString = NULL; + assert(left->fixupsBegin() != left->fixupsEnd()); + for (ld::Fixup::iterator fit = left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + const ld::Atom* cstringAtom = fit->u.target; + assert(cstringAtom->contentType() == ld::Atom::typeCString); + leftString = (char*)cstringAtom->rawContentPointer(); + } + } + const char* rightString = NULL; + assert(right->fixupsBegin() != right->fixupsEnd()); + for (ld::Fixup::iterator fit = right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + const ld::Atom* cstringAtom = fit->u.target; + assert(cstringAtom->contentType() == ld::Atom::typeCString); + rightString = (char*)cstringAtom->rawContentPointer(); + } + } + assert(leftString != NULL); + assert(rightString != NULL); + diff = strcmp(leftString, rightString); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeLiteral4 ) { + // if literal sort by content + uint32_t leftValue = *(uint32_t*)left->rawContentPointer(); + uint32_t rightValue = *(uint32_t*)right->rawContentPointer(); + diff = (leftValue - rightValue); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeCFI ) { + // if __he_frame sort by address + diff = (left->objectAddress() - right->objectAddress()); + if ( diff != 0 ) + return (diff < 0); + } + else if ( left->section().type() == ld::Section::typeNonLazyPointer ) { + // if non-lazy-pointer sort by name + const char* leftString = NULL; + assert(left->fixupsBegin() != left->fixupsEnd()); + for (ld::Fixup::iterator fit = left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByNameUnbound ) { + leftString = fit->u.name; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + leftString = fit->u.target->name(); + } + } + const char* rightString = NULL; + assert(right->fixupsBegin() != right->fixupsEnd()); + for (ld::Fixup::iterator fit = right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByNameUnbound ) { + rightString = fit->u.name; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + rightString = fit->u.target->name(); + } + } + assert(leftString != NULL); + assert(rightString != NULL); + diff = strcmp(leftString, rightString); + if ( diff != 0 ) + return (diff < 0); + } + + // else sort by size + return (left->size() < right->size()); } }; -static void dumpFile(ObjectFile::Reader* reader) +class dumper : public ld::File::AtomHandler +{ +public: + void dump(); + virtual void doAtom(const ld::Atom&); + virtual void doFile(const ld::File&) {} +private: + void dumpAtom(const ld::Atom& atom); + const char* scopeString(const ld::Atom&); + const char* definitionString(const ld::Atom&); + const char* combineString(const ld::Atom&); + const char* inclusionString(const ld::Atom&); + const char* attributeString(const ld::Atom&); + const char* makeName(const ld::Atom& atom); + const char* referenceTargetAtomName(const ld::Fixup* ref); + void dumpFixup(const ld::Fixup* ref); + + uint64_t addressOfFirstAtomInSection(const ld::Section&); + + std::vector<const ld::Atom*> _atoms; +}; + +const char* dumper::scopeString(const ld::Atom& atom) +{ + switch ( (ld::Atom::Scope)atom.scope() ) { + case ld::Atom::scopeTranslationUnit: + return "translation-unit"; + case ld::Atom::scopeLinkageUnit: + return "hidden"; + case ld::Atom::scopeGlobal: + if ( atom.autoHide() ) + return "global but automatically hidden"; + else + return "global"; + } + return "UNKNOWN"; +} + +const char* dumper::definitionString(const ld::Atom& atom) +{ + switch ( (ld::Atom::Definition)atom.definition() ) { + case ld::Atom::definitionRegular: + return "regular"; + case ld::Atom::definitionTentative: + return "tentative"; + case ld::Atom::definitionAbsolute: + return "absolute"; + case ld::Atom::definitionProxy: + return "proxy"; + } + return "UNKNOWN"; +} + +const char* dumper::combineString(const ld::Atom& atom) +{ + switch ( (ld::Atom::Combine)atom.combine() ) { + case ld::Atom::combineNever: + return "never"; + case ld::Atom::combineByName: + return "by-name"; + case ld::Atom::combineByNameAndContent: + return "by-name-and-content"; + case ld::Atom::combineByNameAndReferences: + return "by-name-and-references"; + } + return "UNKNOWN"; +} + +const char* dumper::inclusionString(const ld::Atom& atom) +{ + switch ( (ld::Atom::SymbolTableInclusion)atom.symbolTableInclusion() ) { + case ld::Atom::symbolTableNotIn: + return "not in"; + case ld::Atom::symbolTableNotInFinalLinkedImages: + return "not in final linked images"; + case ld::Atom::symbolTableIn: + return "in"; + case ld::Atom::symbolTableInAndNeverStrip: + return "in and never strip"; + case ld::Atom::symbolTableInAsAbsolute: + return "in as absolute"; + case ld::Atom::symbolTableInWithRandomAutoStripLabel: + return "in as random auto-strip label"; + } + return "UNKNOWN"; +} + + + +const char* dumper::attributeString(const ld::Atom& atom) +{ + static char buffer[256]; + buffer[0] = '\0'; + + if ( atom.dontDeadStrip() ) + strcat(buffer, "dont-dead-strip "); + + if ( atom.isThumb() ) + strcat(buffer, "thumb "); + + if ( atom.isAlias() ) + strcat(buffer, "alias "); + + if ( atom.contentType() == ld::Atom::typeResolver ) + strcat(buffer, "resolver "); + + return buffer; +} + +const char* dumper::makeName(const ld::Atom& atom) +{ + static char buffer[4096]; + strcpy(buffer, "???"); + switch ( atom.symbolTableInclusion() ) { + case ld::Atom::symbolTableNotIn: + if ( atom.contentType() == ld::Atom::typeCString ) { + strcpy(buffer, "cstring="); + strlcat(buffer, (char*)atom.rawContentPointer(), 4096); + } + else if ( atom.section().type() == ld::Section::typeLiteral4 ) { + char temp[16]; + strcpy(buffer, "literal4="); + uint32_t value = *(uint32_t*)atom.rawContentPointer(); + sprintf(temp, "0x%08X", value); + strcat(buffer, temp); + } + else if ( atom.section().type() == ld::Section::typeLiteral8 ) { + char temp[32]; + strcpy(buffer, "literal8="); + uint32_t value1 = *(uint32_t*)atom.rawContentPointer(); + uint32_t value2 = ((uint32_t*)atom.rawContentPointer())[1]; + sprintf(temp, "0x%08X%08X", value1, value2); + strcat(buffer, temp); + } + else if ( atom.section().type() == ld::Section::typeLiteral16 ) { + char temp[64]; + strcpy(buffer, "literal16="); + uint32_t value1 = *(uint32_t*)atom.rawContentPointer(); + uint32_t value2 = ((uint32_t*)atom.rawContentPointer())[1]; + uint32_t value3 = ((uint32_t*)atom.rawContentPointer())[2]; + uint32_t value4 = ((uint32_t*)atom.rawContentPointer())[3]; + sprintf(temp, "0x%08X%08X%08X%08X", value1, value2, value3, value4); + strcat(buffer, temp); + } + else if ( atom.section().type() == ld::Section::typeCStringPointer ) { + assert(atom.fixupsBegin() != atom.fixupsEnd()); + for (ld::Fixup::iterator fit = atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByContentBound ) { + const ld::Atom* cstringAtom = fit->u.target; + if ( (cstringAtom != NULL) && (cstringAtom->contentType() == ld::Atom::typeCString) ) { + strlcpy(buffer, atom.name(), 4096); + strlcat(buffer, "=", 4096); + strlcat(buffer, (char*)cstringAtom->rawContentPointer(), 4096); + } + } + } + } + else if ( atom.section().type() == ld::Section::typeNonLazyPointer ) { + assert(atom.fixupsBegin() != atom.fixupsEnd()); + for (ld::Fixup::iterator fit = atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->binding == ld::Fixup::bindingByNameUnbound ) { + strcpy(buffer, "non-lazy-pointer-to:"); + strlcat(buffer, fit->u.name, 4096); + return buffer; + } + else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + strcpy(buffer, "non-lazy-pointer-to-local:"); + strlcat(buffer, fit->u.target->name(), 4096); + return buffer; + } + } + strlcpy(buffer, atom.name(), 4096); + } + else { + uint64_t sectAddr = addressOfFirstAtomInSection(atom.section()); + sprintf(buffer, "%s@%s+0x%08llX", atom.name(), atom.section().sectionName(), atom.objectAddress()-sectAddr); + } + break; + case ld::Atom::symbolTableNotInFinalLinkedImages: + case ld::Atom::symbolTableIn: + case ld::Atom::symbolTableInAndNeverStrip: + case ld::Atom::symbolTableInAsAbsolute: + case ld::Atom::symbolTableInWithRandomAutoStripLabel: + strlcpy(buffer, atom.name(), 4096); + break; + } + return buffer; +} + +const char* dumper::referenceTargetAtomName(const ld::Fixup* ref) +{ + static char buffer[4096]; + switch ( ref->binding ) { + case ld::Fixup::bindingNone: + return "NO BINDING"; + case ld::Fixup::bindingByNameUnbound: + strcpy(buffer, "by-name("); + strlcat(buffer, ref->u.name, 4096); + strlcat(buffer, ")", 4096); + return buffer; + //return ref->u.name; + case ld::Fixup::bindingByContentBound: + strcpy(buffer, "by-content("); + strlcat(buffer, makeName(*ref->u.target), 4096); + strlcat(buffer, ")", 4096); + return buffer; + case ld::Fixup::bindingDirectlyBound: + strcpy(buffer, "direct("); + strlcat(buffer, makeName(*ref->u.target), 4096); + strlcat(buffer, ")", 4096); + return buffer; + case ld::Fixup::bindingsIndirectlyBound: + return "BOUND INDIRECTLY"; + } + return "BAD BINDING"; +} + + +void dumper::dumpFixup(const ld::Fixup* ref) +{ + if ( ref->weakImport ) { + printf("weak_import "); + } + switch ( (ld::Fixup::Kind)(ref->kind) ) { + case ld::Fixup::kindNone: + printf("none"); + break; + case ld::Fixup::kindNoneFollowOn: + printf("followed by %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinate: + printf("group subordinate %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinateFDE: + printf("group subordinate FDE %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinateLSDA: + printf("group subordinate LSDA %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindNoneGroupSubordinatePersonality: + printf("group subordinate personality %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetTargetAddress: + printf("%s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSubtractTargetAddress: + printf(" - %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindAddAddend: + printf(" + 0x%llX", ref->u.addend); + break; + case ld::Fixup::kindSubtractAddend: + printf(" - 0x%llX", ref->u.addend); + break; + case ld::Fixup::kindSetTargetImageOffset: + printf("imageOffset(%s)", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetTargetSectionOffset: + printf("sectionOffset(%s)", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStore8: + printf(", then store byte"); + break; + case ld::Fixup::kindStoreLittleEndian16: + printf(", then store 16-bit little endian"); + break; + case ld::Fixup::kindStoreLittleEndianLow24of32: + printf(", then store low 24-bit little endian"); + break; + case ld::Fixup::kindStoreLittleEndian32: + printf(", then store 32-bit little endian"); + break; + case ld::Fixup::kindStoreLittleEndian64: + printf(", then store 64-bit little endian"); + break; + case ld::Fixup::kindStoreBigEndian16: + printf(", then store 16-bit big endian"); + break; + case ld::Fixup::kindStoreBigEndianLow24of32: + printf(", then store low 24-bit big endian"); + break; + case ld::Fixup::kindStoreBigEndian32: + printf(", then store 32-bit big endian"); + break; + case ld::Fixup::kindStoreBigEndian64: + printf(", then store 64-bit big endian"); + break; + case ld::Fixup::kindStorePPCBranch24: + printf(", then store as PPC branch24"); + break; + case ld::Fixup::kindStorePPCBranch14: + printf(", then store as PPC branch14"); + break; + case ld::Fixup::kindStorePPCPicLow14: + printf(", then store as PPC low14 pic"); + break; + case ld::Fixup::kindStorePPCPicLow16: + printf(", then store as PPC low14 pic"); + break; + case ld::Fixup::kindStorePPCPicHigh16AddLow: + printf(", then store as PPC high16 pic"); + break; + case ld::Fixup::kindStorePPCAbsLow14: + printf(", then store as PPC low14 abs"); + break; + case ld::Fixup::kindStorePPCAbsLow16: + printf(", then store as PPC low14 abs"); + break; + case ld::Fixup::kindStorePPCAbsHigh16AddLow: + printf(", then store as PPC high16 abs"); + break; + case ld::Fixup::kindStorePPCAbsHigh16: + printf(", then store as PPC high16 abs, no carry"); + break; + case ld::Fixup::kindStoreX86BranchPCRel8: + printf(", then store as x86 8-bit pcrel branch"); + break; + case ld::Fixup::kindStoreX86BranchPCRel32: + printf(", then store as x86 32-bit pcrel branch"); + break; + case ld::Fixup::kindStoreX86PCRel8: + printf(", then store as x86 8-bit pcrel"); + break; + case ld::Fixup::kindStoreX86PCRel16: + printf(", then store as x86 16-bit pcrel"); + break; + case ld::Fixup::kindStoreX86PCRel32: + printf(", then store as x86 32-bit pcrel"); + break; + case ld::Fixup::kindStoreX86PCRel32_1: + printf(", then store as x86 32-bit pcrel from +1"); + break; + case ld::Fixup::kindStoreX86PCRel32_2: + printf(", then store as x86 32-bit pcrel from +2"); + break; + case ld::Fixup::kindStoreX86PCRel32_4: + printf(", then store as x86 32-bit pcrel from +4"); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + printf(", then store as x86 32-bit pcrel GOT load"); + break; + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + printf(", then store as x86 32-bit pcrel GOT load -> LEA"); + break; + case ld::Fixup::kindStoreX86PCRel32GOT: + printf(", then store as x86 32-bit pcrel GOT access"); + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + printf(", then store as x86 32-bit pcrel TLV load"); + break; + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + printf(", then store as x86 32-bit pcrel TLV load"); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoad: + printf(", then store as x86 32-bit absolute TLV load"); + break; + case ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA: + printf(", then store as x86 32-bit absolute TLV load -> LEA"); + break; + case ld::Fixup::kindStoreARMBranch24: + printf(", then store as ARM 24-bit pcrel branch"); + break; + case ld::Fixup::kindStoreThumbBranch22: + printf(", then store as Thumb 22-bit pcrel branch"); + break; + case ld::Fixup::kindStoreARMLoad12: + printf(", then store as ARM 12-bit pcrel load"); + break; + case ld::Fixup::kindStoreARMLow16: + printf(", then store low-16 in ARM movw"); + break; + case ld::Fixup::kindStoreARMHigh16: + printf(", then store high-16 in ARM movt"); + break; + case ld::Fixup::kindStoreThumbLow16: + printf(", then store low-16 in Thumb movw"); + break; + case ld::Fixup::kindStoreThumbHigh16: + printf(", then store high-16 in Thumb movt"); + break; + case ld::Fixup::kindDtraceExtra: + printf("dtrace static probe extra info"); + break; + case ld::Fixup::kindStoreX86DtraceCallSiteNop: + printf("x86 dtrace static probe site"); + break; + case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: + printf("x86 dtrace static is-enabled site"); + break; + case ld::Fixup::kindStorePPCDtraceCallSiteNop: + printf("ppc dtrace static probe site"); + break; + case ld::Fixup::kindStorePPCDtraceIsEnableSiteClear: + printf("ppc dtrace static is-enabled site"); + break; + case ld::Fixup::kindStoreARMDtraceCallSiteNop: + printf("ARM dtrace static probe site"); + break; + case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + printf("ARM dtrace static is-enabled site"); + break; + case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + printf("Thumb dtrace static probe site"); + break; + case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + printf("Thumb dtrace static is-enabled site"); + break; + case ld::Fixup::kindLazyTarget: + printf("lazy reference to external symbol %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetLazyOffset: + printf("offset of lazy binding info for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + printf("store 64-bit little endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian32: + printf("store 32-bit big endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressBigEndian64: + printf("store 64-bit big endian address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + printf("x86 store 32-bit pc-rel address of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: + printf("x86 store 32-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + printf("x86 store 32-bit pc-rel GOT load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + printf("x86 store 32-bit pc-rel lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + printf("x86 store 32-bit pc-rel TLV load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: + printf("x86 store 32-bit pc-rel TLV lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: + printf("x86 store 32-bit absolute TLV load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: + printf("x86 store 32-bit absolute TLV lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARMBranch24: + printf("ARM store 24-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + printf("Thumb store 22-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARMLoad12: + printf("ARM store 12-bit pc-rel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressPPCBranch24: + printf("PowerPC store 24-bit pc-rel load of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindSetTargetTLVTemplateOffset: + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: + case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: + printf("tlv template offset of %s", referenceTargetAtomName(ref)); + //default: + // printf("unknown fixup"); + // break; + } +} + +uint64_t dumper::addressOfFirstAtomInSection(const ld::Section& sect) +{ + uint64_t lowestAddr = (uint64_t)(-1); + for (std::vector<const ld::Atom*>::iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( &atom->section() == § ) { + if ( atom->objectAddress() < lowestAddr ) + lowestAddr = atom->objectAddress(); + } + } + return lowestAddr; +} + +void dumper::doAtom(const ld::Atom& atom) +{ + if ( (sMatchName != NULL) && (strcmp(sMatchName, atom.name()) != 0) ) + return; + _atoms.push_back(&atom); +} + +void dumper::dump() +{ + if ( sSort ) + std::sort(_atoms.begin(), _atoms.end(), AtomSorter()); + + for (std::vector<const ld::Atom*>::iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + this->dumpAtom(**it); + } +} + +void dumper::dumpAtom(const ld::Atom& atom) +{ + printf("name: %s\n", makeName(atom)); + printf("size: 0x%0llX\n", atom.size()); + printf("align: %u mod %u\n", atom.alignment().modulus, (1 << atom.alignment().powerOf2) ); + printf("scope: %s\n", scopeString(atom)); + if ( sShowDefinitionKind ) + printf("def: %s\n", definitionString(atom)); + if ( sShowCombineKind ) + printf("combine: %s\n", combineString(atom)); + printf("symbol: %s\n", inclusionString(atom)); + printf("attrs: %s\n", attributeString(atom)); + if ( sShowSection ) + printf("section: %s,%s\n", atom.section().segmentName(), atom.section().sectionName()); + if ( atom.beginUnwind() != atom.endUnwind() ) { + printf("unwind: 0x%08X\n", atom.beginUnwind()->unwindInfo); + } + if ( atom.contentType() == ld::Atom::typeCString ) { + uint8_t buffer[atom.size()+2]; + atom.copyRawContent(buffer); + buffer[atom.size()] = '\0'; + printf("content: \"%s\"\n", buffer); + } + if ( atom.fixupsBegin() != atom.fixupsEnd() ) { + printf("fixups:\n"); + for (unsigned int off=0; off < atom.size()+1; ++off) { + for (ld::Fixup::iterator it = atom.fixupsBegin(); it != atom.fixupsEnd(); ++it) { + if ( it->offsetInAtom == off ) { + switch ( it->clusterSize ) { + case ld::Fixup::k1of1: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + break; + case ld::Fixup::k1of2: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + break; + case ld::Fixup::k1of3: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + break; + case ld::Fixup::k1of4: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + break; + case ld::Fixup::k1of5: + printf(" 0x%04X ", it->offsetInAtom); + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + ++it; + dumpFixup(it); + break; + default: + printf(" BAD CLUSTER SIZE: cluster=%d\n", it->clusterSize); + } + printf("\n"); + } + } + } + } + if ( atom.beginLineInfo() != atom.endLineInfo() ) { + printf("line info:\n"); + for (ld::Atom::LineInfo::iterator it = atom.beginLineInfo(); it != atom.endLineInfo(); ++it) { + printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName); + } + } + + printf("\n"); +} + +static void dumpFile(ld::relocatable::File* file) { // stabs debug info - if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) { - std::vector<ObjectFile::Reader::Stab>* stabs = reader->getStabs(); + if ( sDumpStabs && (file->debugInfo() == ld::relocatable::File::kDebugInfoStabs) ) { + const std::vector<ld::relocatable::File::Stab>* stabs = file->stabs(); if ( stabs != NULL ) dumpStabs(stabs); } - + // dump atoms + dumper d; + file->forEachAtom(d); + d.dump(); + +#if 0 // get all atoms std::vector<ObjectFile::Atom*> atoms = reader->getAtoms(); @@ -387,10 +1066,11 @@ static void dumpFile(ObjectFile::Reader* reader) else dumpAtom(*it); } +#endif } -static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options) +static ld::relocatable::File* createReader(const char* path) { struct stat stat_buf; @@ -400,7 +1080,11 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read ::fstat(fd, &stat_buf); uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); ::close(fd); + if ( p == (uint8_t*)(-1) ) + throwf("cannot mmap file: %s", path); const mach_header* mh = (mach_header*)p; + uint64_t fileLen = stat_buf.st_size; + bool foundFatSlice = false; if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { const struct fat_header* fh = (struct fat_header*)p; const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); @@ -409,28 +1093,48 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) { p = p + OSSwapBigToHostInt32(archs[i].offset); mh = (struct mach_header*)p; + fileLen = OSSwapBigToHostInt32(archs[i].size); + foundFatSlice = true; break; } } } } - if ( mach_o::relocatable::Reader<x86>::validFile(p) ) - return new mach_o::relocatable::Reader<x86>::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader<ppc>::validFile(p) ) - return new mach_o::relocatable::Reader<ppc>::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader<ppc64>::validFile(p) ) - return new mach_o::relocatable::Reader<ppc64>::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader<x86_64>::validFile(p) ) - return new mach_o::relocatable::Reader<x86_64>::Reader(p, path, 0, options, 0); - else if ( mach_o::relocatable::Reader<arm>::validFile(p) ) - return new mach_o::relocatable::Reader<arm>::Reader(p, path, 0, options, 0); -#if LTO_SUPPORT - if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) { - return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0); + + mach_o::relocatable::ParserOptions objOpts; + objOpts.architecture = sPreferredArch; + objOpts.objSubtypeMustMatch = false; + objOpts.logAllFiles = false; + objOpts.convertUnwindInfo = true; + objOpts.subType = sPreferredSubArch; +#if 1 + if ( ! foundFatSlice ) { + cpu_type_t archOfObj; + cpu_subtype_t subArchOfObj; + if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj) ) { + objOpts.architecture = archOfObj; + objOpts.subType = subArchOfObj; + } } -#endif - + + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, 0, objOpts); + if ( objResult != NULL ) + return objResult; + + // see if it is an llvm object file + objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, 0, sPreferredArch, sPreferredSubArch, false); + if ( objResult != NULL ) + return objResult; + throwf("not a mach-o object file: %s", path); +#else + // for peformance testing + for (int i=0; i < 500; ++i ) { + ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, 0, objOpts); + delete objResult; + } + exit(0); +#endif } static @@ -439,6 +1143,9 @@ usage() { fprintf(stderr, "ObjectDump options:\n" "\t-no_content\tdon't dump contents\n" + "\t-no_section\tdon't dump section name\n" + "\t-no_defintion\tdon't dump definition kind\n" + "\t-no_combine\tdon't dump combine mode\n" "\t-stabs\t\tdump stabs\n" "\t-arch aaa\tonly dump info about arch aaa\n" "\t-only sym\tonly dump info about sym\n" @@ -454,8 +1161,6 @@ int main(int argc, const char* argv[]) return 0; } - ObjectFile::ReaderOptions options; - options.fAddCompactUnwindEncoding = true; try { for(int i=1; i < argc; ++i) { const char* arg = argv[i]; @@ -472,6 +1177,15 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-no_sort") == 0 ) { sSort = false; } + else if ( strcmp(arg, "-no_section") == 0 ) { + sShowSection = false; + } + else if ( strcmp(arg, "-no_definition") == 0 ) { + sShowDefinitionKind = false; + } + else if ( strcmp(arg, "-no_combine") == 0 ) { + sShowCombineKind = false; + } else if ( strcmp(arg, "-arch") == 0 ) { const char* arch = ++i<argc? argv[i]: ""; if ( strcmp(arch, "ppc64") == 0 ) @@ -520,7 +1234,7 @@ int main(int argc, const char* argv[]) } } else { - ObjectFile::Reader* reader = createReader(arg, options); + ld::relocatable::File* reader = createReader(arg); dumpFile(reader); } } diff --git a/src/other/dyldinfo.cpp b/src/other/dyldinfo.cpp index 5c42471..a6988ec 100644 --- a/src/other/dyldinfo.cpp +++ b/src/other/dyldinfo.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -46,12 +46,16 @@ static bool printLazyBind = false; static bool printOpcodes = false; static bool printExport = false; static bool printExportGraph = false; +static bool printExportNodes = false; +static bool printSharedRegion = false; +static bool printFunctionStarts = false; +static bool printDylibs = false; static cpu_type_t sPreferredArch = CPU_TYPE_I386; static cpu_type_t sPreferredSubArch = 0; - __attribute__((noreturn)) -void throwf(const char* format, ...) +__attribute__((noreturn)) +static void throwf(const char* format, ...) { va_list list; char* p; @@ -98,18 +102,25 @@ private: void printLazyBindingOpcodes(); void printExportInfo(); void printExportInfoGraph(); + void printExportInfoNodes(); void printRelocRebaseInfo(); void printSymbolTableExportInfo(); void printClassicLazyBindingInfo(); void printClassicBindingInfo(); + void printSharedRegionInfo(); + void printFunctionStartsInfo(); + void printDylibsInfo(); + void printFunctionStartLine(uint64_t addr); + const uint8_t* printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind); pint_t relocBase(); const char* relocTypeName(uint8_t r_type); uint8_t segmentIndexForAddress(pint_t addr); - void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, - char* cummulativeString, int curStrOffset); void processExportGraphNode(const uint8_t* const start, const uint8_t* const end, const uint8_t* parent, const uint8_t* p, char* cummulativeString, int curStrOffset); + void gatherNodeStarts(const uint8_t* const start, const uint8_t* const end, + const uint8_t* parent, const uint8_t* p, + std::vector<uint32_t>& nodeStarts); const char* rebaseTypeName(uint8_t type); const char* bindTypeName(uint8_t type); pint_t segStartAddress(uint8_t segIndex); @@ -119,7 +130,7 @@ private: const char* ordinalName(int libraryOrdinal); const char* classicOrdinalName(int libraryOrdinal); pint_t* mappedAddressForVMAddress(pint_t vmaddress); - + const char* symbolNameForAddress(uint64_t); const char* fPath; @@ -130,6 +141,8 @@ private: const macho_nlist<P>* fSymbols; uint32_t fSymbolCount; const macho_dyld_info_command<P>* fInfo; + const macho_linkedit_data_command<P>* fSharedRegionInfo; + const macho_linkedit_data_command<P>* fFunctionStartsInfo; uint64_t fBaseAddress; const macho_dysymtab_command<P>* fDynamicSymbolTable; const macho_segment_command<P>* fFirstSegment; @@ -137,6 +150,7 @@ private: bool fWriteableSegmentWithAddrOver4G; std::vector<const macho_segment_command<P>*>fSegments; std::vector<const char*> fDylibs; + std::vector<const macho_dylib_command<P>*> fDylibLoadCommands; }; @@ -235,6 +249,7 @@ template <typename A> DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) : fHeader(NULL), fLength(fileLength), fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL), + fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL), fBaseAddress(0), fDynamicSymbolTable(NULL), fFirstSegment(NULL), fFirstWritableSegment(NULL), fWriteableSegmentWithAddrOver4G(false) { @@ -281,9 +296,11 @@ DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: case LC_LAZY_LOAD_DYLIB: { const macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; + fDylibLoadCommands.push_back(dylib); const char* lastSlash = strrchr(dylib->name(), '/'); const char* leafName = (lastSlash != NULL) ? lastSlash+1 : dylib->name(); const char* firstDot = strchr(leafName, '.'); @@ -309,6 +326,12 @@ DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen fStringsEnd = fStrings + symtab->strsize(); } break; + case LC_SEGMENT_SPLIT_INFO: + fSharedRegionInfo = (macho_linkedit_data_command<P>*)cmd; + break; + case LC_FUNCTION_STARTS: + fFunctionStartsInfo = (macho_linkedit_data_command<P>*)cmd; + break; } cmd = (const macho_load_command<P>*)endOfCmd; } @@ -347,6 +370,14 @@ DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen } if ( printExportGraph ) printExportInfoGraph(); + if ( printExportNodes ) + printExportInfoNodes(); + if ( printSharedRegion ) + printSharedRegionInfo(); + if ( printFunctionStarts ) + printFunctionStartsInfo(); + if ( printDylibs ) + printDylibsInfo(); } static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) @@ -522,6 +553,8 @@ const char* DyldInfoPrinter<A>::ordinalName(int libraryOrdinal) template <typename A> const char* DyldInfoPrinter<A>::classicOrdinalName(int libraryOrdinal) { + if ( (fHeader->flags() & MH_TWOLEVEL) == 0 ) + return "flat-namespace"; switch ( libraryOrdinal) { case SELF_LIBRARY_ORDINAL: return "this-image"; @@ -811,7 +844,7 @@ void DyldInfoPrinter<A>::printBindingInfo() } else { printf("bind information:\n"); - printf("segment section address type weak addend dylib symbol\n"); + printf("segment section address type addend dylib symbol\n"); const uint8_t* p = (uint8_t*)fHeader + fInfo->bind_off(); const uint8_t* end = &p[fInfo->bind_size()]; @@ -861,7 +894,7 @@ void DyldInfoPrinter<A>::printBindingInfo() ++p; ++p; if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ) - weak_import = "weak"; + weak_import = " (weak import)"; else weak_import = ""; break; @@ -882,22 +915,22 @@ void DyldInfoPrinter<A>::printBindingInfo() segOffset += read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += read_uleb128(p, end) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += immediate*sizeof(pint_t) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { - printf("%-7s %-16s 0x%08llX %10s %4s %5lld %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, weak_import, addend, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX %10s %5lld %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName, addend, fromDylib, symbolName, weak_import ); segOffset += skip + sizeof(pint_t); } break; @@ -1019,6 +1052,7 @@ void DyldInfoPrinter<A>::printLazyBindingInfo() pint_t segStartAddr = 0; const char* segName = "??"; const char* typeName = "??"; + const char* weak_import = ""; for (const uint8_t* p=start; p < end; ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; uint8_t opcode = *p & BIND_OPCODE_MASK; @@ -1050,6 +1084,10 @@ void DyldInfoPrinter<A>::printLazyBindingInfo() while (*p != '\0') ++p; ++p; + if ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ) + weak_import = " (weak import)"; + else + weak_import = ""; break; case BIND_OPCODE_SET_TYPE_IMM: type = immediate; @@ -1068,7 +1106,7 @@ void DyldInfoPrinter<A>::printLazyBindingInfo() segOffset += read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, lazy_offset, fromDylib, symbolName ); + printf("%-7s %-16s 0x%08llX 0x%04X %-16s %s%s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, lazy_offset, fromDylib, symbolName, weak_import); segOffset += sizeof(pint_t); break; default: @@ -1177,35 +1215,6 @@ void DyldInfoPrinter<A>::printLazyBindingOpcodes() } - -template <typename A> -void DyldInfoPrinter<A>::processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, - char* cummulativeString, int curStrOffset) -{ - const uint8_t terminalSize = *p++; - const uint8_t* children = p + terminalSize; - if ( terminalSize != 0 ) { - uint32_t flags = read_uleb128(p, end); - uint64_t address = read_uleb128(p, end); - if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) - fprintf(stdout, "0x%08llX [weak_def] %s\n", address, cummulativeString); - else - fprintf(stdout, "0x%08llX %s\n", address, cummulativeString); - } - const uint8_t childrenCount = *children++; - const uint8_t* s = children; - for (uint8_t i=0; i < childrenCount; ++i) { - int edgeStrLen = 0; - while (*s != '\0') { - cummulativeString[curStrOffset+edgeStrLen] = *s++; - ++edgeStrLen; - } - cummulativeString[curStrOffset+edgeStrLen] = *s++; - uint32_t childNodeOffet = read_uleb128(s, end); - processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen); - } -} - struct SortExportsByAddress { bool operator()(const mach_o::trie::Entry& left, const mach_o::trie::Entry& right) @@ -1228,10 +1237,26 @@ void DyldInfoPrinter<A>::printExportInfo() parseTrie(start, end, list); //std::sort(list.begin(), list.end(), SortExportsByAddress()); for (std::vector<mach_o::trie::Entry>::iterator it=list.begin(); it != list.end(); ++it) { - const char* flags = ""; - if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) - flags = "[weak_def] "; - fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name); + if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( it->importName[0] == '\0' ) + fprintf(stdout, "[re-export] %s from dylib=%llu\n", it->name, it->other); + else + fprintf(stdout, "[re-export] %s from dylib=%llu named=%s\n", it->name, it->other, it->importName); + } + else { + const char* flags = ""; + if ( it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) + flags = "[weak_def] "; + else if ( (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL ) + flags = "[per-thread] "; + if ( it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + flags = "[resolver] "; + fprintf(stdout, "0x%08llX %s%s (resolver=0x%08llX)\n", fBaseAddress+it->address, flags, it->name, it->other); + } + else { + fprintf(stdout, "0x%08llX %s%s\n", fBaseAddress+it->address, flags, it->name); + } + } } } } @@ -1243,13 +1268,27 @@ void DyldInfoPrinter<A>::processExportGraphNode(const uint8_t* const start, cons char* cummulativeString, int curStrOffset) { const uint8_t* const me = p; - const uint8_t terminalSize = *p++; + const uint8_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { uint32_t flags = read_uleb128(p, end); - (void)flags; // currently unused - uint64_t address = read_uleb128(p, end); - printf("\tnode%03ld [ label=%s,addr0x%08llX ];\n", (long)(me-start), cummulativeString, address); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + uint64_t ordinal = read_uleb128(p, end); + const char* importName = (const char*)p; + while (*p != '\0') + ++p; + ++p; + if ( *importName == '\0' ) + printf("\tnode%03ld [ label=%s,re-export from dylib=%llu ];\n", (long)(me-start), cummulativeString, ordinal); + else + printf("\tnode%03ld [ label=%s,re-export %s from dylib=%llu ];\n", (long)(me-start), cummulativeString, importName, ordinal); + } + else { + uint64_t address = read_uleb128(p, end); + printf("\tnode%03ld [ label=%s,addr0x%08llX ];\n", (long)(me-start), cummulativeString, address); + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + read_uleb128(p, end); + } } else { printf("\tnode%03ld;\n", (long)(me-start)); @@ -1286,6 +1325,222 @@ void DyldInfoPrinter<A>::printExportInfoGraph() } } +template <typename A> +void DyldInfoPrinter<A>::gatherNodeStarts(const uint8_t* const start, const uint8_t* const end, + const uint8_t* parent, const uint8_t* p, + std::vector<uint32_t>& nodeStarts) +{ + nodeStarts.push_back(p-start); + const uint8_t terminalSize = read_uleb128(p, end); + const uint8_t* children = p + terminalSize; + + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + // skip over edge string + while (*s != '\0') + ++s; + ++s; + uint32_t childNodeOffet = read_uleb128(s, end); + gatherNodeStarts(start, end, start, start+childNodeOffet, nodeStarts); + } +} + + +template <typename A> +void DyldInfoPrinter<A>::printExportInfoNodes() +{ + if ( (fInfo == NULL) || (fInfo->export_off() == 0) ) { + printf("no compressed export info\n"); + } + else { + const uint8_t* start = (uint8_t*)fHeader + fInfo->export_off(); + const uint8_t* end = &start[fInfo->export_size()]; + std::vector<uint32_t> nodeStarts; + gatherNodeStarts(start, end, start, start, nodeStarts); + std::sort(nodeStarts.begin(), nodeStarts.end()); + for (std::vector<uint32_t>::const_iterator it=nodeStarts.begin(); it != nodeStarts.end(); ++it) { + printf("0x%04X: ", *it); + const uint8_t* p = start + *it; + uint64_t exportInfoSize = read_uleb128(p, end); + if ( exportInfoSize != 0 ) { + // print terminal info + uint64_t flags = read_uleb128(p, end); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + uint64_t ordinal = read_uleb128(p, end); + const char* importName = (const char*)p; + while (*p != '\0') + ++p; + ++p; + if ( strlen(importName) == 0 ) + printf("[flags=REEXPORT ordinal=%llu] ", ordinal); + else + printf("[flags=REEXPORT ordinal=%llu import=%s] ", ordinal, importName); + } + else if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + uint64_t stub = read_uleb128(p, end); + uint64_t resolver = read_uleb128(p, end); + printf("[flags=STUB_AND_RESOLVER stub=0x%06llX resolver=0x%06llX] ", stub, resolver); + } + else { + uint64_t address = read_uleb128(p, end); + if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + printf("[addr=0x%06llX] ", address); + else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL) + printf("[flags=THREAD_LOCAL addr=0x%06llX] ", address); + else + printf("[flags=0x%llX addr=0x%06llX] ", flags, address); + } + } + // print child edges + const uint8_t childrenCount = *p++; + for (uint8_t i=0; i < childrenCount; ++i) { + const char* edgeName = (const char*)p; + while (*p != '\0') + ++p; + ++p; + uint32_t childNodeOffet = read_uleb128(p, end); + printf("%s->0x%04X", edgeName, childNodeOffet); + if ( i < (childrenCount-1) ) + printf(", "); + } + printf("\n"); + } + } +} + + + +template <typename A> +const uint8_t* DyldInfoPrinter<A>::printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind) +{ + const char* kindStr = "??"; + switch (kind) { + case 1: + kindStr = "32-bit pointer"; + break; + case 2: + kindStr = "64-bit pointer"; + break; + case 3: + kindStr = "ppc hi16"; + break; + case 4: + kindStr = "32-bit offset to IMPORT"; + break; + } + uint64_t address = 0; + uint64_t delta = 0; + uint32_t shift = 0; + bool more = true; + do { + uint8_t byte = *p++; + delta |= ((byte & 0x7F) << shift); + shift += 7; + if ( byte < 0x80 ) { + if ( delta != 0 ) { + address += delta; + printf("0x%0llX %s\n", address+fBaseAddress, kindStr); + delta = 0; + shift = 0; + } + else { + more = false; + } + } + } while (more); + return p; +} + +template <typename A> +void DyldInfoPrinter<A>::printSharedRegionInfo() +{ + if ( (fSharedRegionInfo == NULL) || (fSharedRegionInfo->datasize() == 0) ) { + printf("no shared region info\n"); + } + else { + const uint8_t* infoStart = (uint8_t*)fHeader + fSharedRegionInfo->dataoff(); + const uint8_t* infoEnd = &infoStart[fSharedRegionInfo->datasize()]; + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { + uint8_t kind = *p++; + p = this->printSharedRegionInfoForEachULEB128Address(p, kind); + } + + } +} + +template <> +void DyldInfoPrinter<arm>::printFunctionStartLine(uint64_t addr) +{ + if ( addr & 1 ) + printf("0x%0llX [thumb] %s\n", (addr & -2), symbolNameForAddress(addr & -2)); + else + printf("0x%0llX %s\n", addr, symbolNameForAddress(addr)); +} + +template <typename A> +void DyldInfoPrinter<A>::printFunctionStartLine(uint64_t addr) +{ + printf("0x%0llX %s\n", addr, symbolNameForAddress(addr)); +} + + +template <typename A> +void DyldInfoPrinter<A>::printFunctionStartsInfo() +{ + if ( (fFunctionStartsInfo == NULL) || (fFunctionStartsInfo->datasize() == 0) ) { + printf("no function starts info\n"); + } + else { + const uint8_t* infoStart = (uint8_t*)fHeader + fFunctionStartsInfo->dataoff(); + const uint8_t* infoEnd = &infoStart[fFunctionStartsInfo->datasize()]; + uint64_t address = fBaseAddress; + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd); ) { + uint64_t delta = 0; + uint32_t shift = 0; + bool more = true; + do { + uint8_t byte = *p++; + delta |= ((byte & 0x7F) << shift); + shift += 7; + if ( byte < 0x80 ) { + address += delta; + printFunctionStartLine(address); + more = false; + } + } while (more); + } + } +} + +template <typename A> +void DyldInfoPrinter<A>::printDylibsInfo() +{ + printf("attributes dependent dylibs\n"); + for(typename std::vector<const macho_dylib_command<P>*>::iterator it = fDylibLoadCommands.begin(); it != fDylibLoadCommands.end(); ++it) { + const macho_dylib_command<P>* dylib = *it; + const char* attribute = ""; + switch ( dylib->cmd() ) { + case LC_LOAD_WEAK_DYLIB: + attribute = "weak_import"; + break; + case LC_REEXPORT_DYLIB: + attribute = "re-export"; + break; + case LC_LOAD_UPWARD_DYLIB: + attribute = "upward"; + break; + case LC_LAZY_LOAD_DYLIB: + attribute = "lazy_load"; + break; + case LC_LOAD_DYLIB: + default: + break; + } + printf(" %-12s %s\n", attribute, dylib->name()); + } +} + template <> ppc::P::uint_t DyldInfoPrinter<ppc>::relocBase() @@ -1465,6 +1720,27 @@ void DyldInfoPrinter<A>::printSymbolTableExportInfo() } } +template <typename A> +const char* DyldInfoPrinter<A>::symbolNameForAddress(uint64_t addr) +{ + // find exact match in globals + const macho_nlist<P>* lastExport = &fSymbols[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()]; + for (const macho_nlist<P>* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) { + if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { + return &fStrings[sym->n_strx()]; + } + } + // find exact match in local symbols + const macho_nlist<P>* lastLocal = &fSymbols[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()]; + for (const macho_nlist<P>* sym = &fSymbols[fDynamicSymbolTable->ilocalsym()]; sym < lastLocal; ++sym) { + if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { + return &fStrings[sym->n_strx()]; + } + } + + + return "?"; +} template <typename A> void DyldInfoPrinter<A>::printClassicBindingInfo() @@ -1679,12 +1955,14 @@ static void dump(const char* path) static void usage() { fprintf(stderr, "Usage: dyldinfo [-arch <arch>] <options> <mach-o file>\n" + "\t-dylibs print dependent dylibs\n" "\t-rebase print addresses dyld will adjust if file not loaded at preferred address\n" "\t-bind print addresses dyld will set based on symbolic lookups\n" "\t-weak_bind print symbols which dyld must coalesce\n" "\t-lazy_bind print addresses dyld will lazily set on first use\n" "\t-export print addresses of all symbols this file exports\n" "\t-opcodes print opcodes used to generate the rebase and binding information\n" + "\t-function_starts print table of function start addresses\n" "\t-export_dot print a GraphViz .dot file of the exported symbols trie\n" ); } @@ -1754,6 +2032,18 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-export_dot") == 0 ) { printExportGraph = true; } + else if ( strcmp(arg, "-export_trie_nodes") == 0 ) { + printExportNodes = true; + } + else if ( strcmp(arg, "-shared_region") == 0 ) { + printSharedRegion = true; + } + else if ( strcmp(arg, "-function_starts") == 0 ) { + printFunctionStarts = true; + } + else if ( strcmp(arg, "-dylibs") == 0 ) { + printDylibs = true; + } else { throwf("unknown option: %s\n", arg); } diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index 68fdbb8..295fa19 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * Copyright (c) 2006-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -52,6 +52,26 @@ void throwf(const char* format, ...) throw t; } +static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throwf("malformed uleb128"); + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throwf("uleb128 too big"); + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} template <typename A> class MachOChecker @@ -88,6 +108,8 @@ private: void checkLocalReloation(const macho_relocation_info<P>* reloc); pint_t relocBase(); bool addressInWritableSegment(pint_t address); + bool hasTextRelocInRange(pint_t start, pint_t end); + pint_t segStartAddress(uint8_t segIndex); const char* fPath; const macho_header<P>* fHeader; @@ -106,7 +128,9 @@ private: bool fWriteableSegmentWithAddrOver4G; const macho_segment_command<P>* fFirstSegment; const macho_segment_command<P>* fFirstWritableSegment; + const macho_dyld_info_command<P>* fDyldInfo; uint32_t fSectionCount; + std::vector<const macho_segment_command<P>*>fSegments; }; @@ -211,7 +235,7 @@ template <typename A> MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0), fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0), - fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fSectionCount(0) + fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fDyldInfo(NULL), fSectionCount(0) { // sanity check if ( ! validFile(fileContent) ) @@ -241,7 +265,7 @@ void MachOChecker<A>::checkMachHeader() throw "sizeofcmds in mach_header is larger than file"; uint32_t flags = fHeader->flags(); - const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFC00000; + const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFE000000; if ( flags & invalidBits ) throw "invalid bits in mach_header flags"; if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) @@ -287,8 +311,14 @@ void MachOChecker<A>::checkLoadCommands() case LC_REEXPORT_DYLIB: case LC_SEGMENT_SPLIT_INFO: case LC_CODE_SIGNATURE: + case LC_LOAD_UPWARD_DYLIB: + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_FUNCTION_STARTS: + break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: + fDyldInfo = (macho_dyld_info_command<P>*)cmd; break; case LC_ENCRYPTION_INFO: encryption_info = (macho_encryption_info_command<P>*)cmd; @@ -312,6 +342,7 @@ void MachOChecker<A>::checkLoadCommands() for (uint32_t i = 0; i < cmd_count; ++i) { if ( cmd->cmd() == macho_segment_command<P>::CMD ) { const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; + fSegments.push_back(segCmd); if ( segCmd->cmdsize() != (sizeof(macho_segment_command<P>) + segCmd->nsects() * sizeof(macho_section_content<P>)) ) throw "invalid segment load command size"; @@ -381,7 +412,9 @@ void MachOChecker<A>::checkLoadCommands() throwf("section %s vm address not within segment", sect->sectname()); if ( (sect->addr()+sect->size()) > endAddr ) throwf("section %s vm address not within segment", sect->sectname()); - if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) { + if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL) + && ((sect->flags() & SECTION_TYPE) != S_THREAD_LOCAL_ZEROFILL) + && (segCmd->filesize() != 0) ) { if ( sect->offset() < startOffset ) throwf("section %s file offset not within segment", sect->sectname()); if ( (sect->offset()+sect->size()) > endOffset ) @@ -470,7 +503,7 @@ void MachOChecker<A>::checkLoadCommands() if ( isStaticExecutable ) throw "LC_DYSYMTAB should not be used in static executable"; foundDynamicSymTab = true; - fDynamicSymbolTable = (struct macho_dysymtab_command<P>*)cmd; + fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd; fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff()); fIndirectTableCount = fDynamicSymbolTable->nindirectsyms(); if ( fIndirectTableCount != 0 ) { @@ -507,7 +540,7 @@ void MachOChecker<A>::checkLoadCommands() { if ( isStaticExecutable ) throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable"; - const macho_linkedit_data_command<P>* info = (struct macho_linkedit_data_command<P>*)cmd; + const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd; if ( info->dataoff() < linkEditSegment->fileoff() ) throw "split seg info not in __LINKEDIT"; if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) @@ -518,6 +551,19 @@ void MachOChecker<A>::checkLoadCommands() throw "split seg info size not a multiple of pointer size"; } break; + case LC_FUNCTION_STARTS: + { + const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd; + if ( info->dataoff() < linkEditSegment->fileoff() ) + throw "function starts data not in __LINKEDIT"; + if ( (info->dataoff()+info->datasize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) ) + throw "function starts data not in __LINKEDIT"; + if ( (info->dataoff() % sizeof(pint_t)) != 0 ) + throw "function starts data table not pointer aligned"; + if ( (info->datasize() % sizeof(pint_t)) != 0 ) + throw "function starts data size not a multiple of pointer size"; + } + break; } cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -543,6 +589,9 @@ void MachOChecker<A>::checkSection(const macho_segment_command<P>* segCmd, const template <typename A> void MachOChecker<A>::checkIndirectSymbolTable() { + // static executables don't have indirect symbol table + if ( fDynamicSymbolTable == NULL ) + return; const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); const uint32_t cmd_count = fHeader->ncmds(); const macho_load_command<P>* cmd = cmds; @@ -596,7 +645,11 @@ void MachOChecker<A>::checkSymbolTable() //fprintf(stderr, "sym[%d] = %s\n", i, symName); if ( externalNames.find(symName) != externalNames.end() ) throwf("duplicate external symbol: %s", symName); - externalNames.insert(symName); + if ( (p->n_type() & N_EXT) == 0 ) + throwf("non-external symbol in external symbol range: %s", symName); + // don't add N_INDR to externalNames because there is likely an undefine with same name + if ( (p->n_type() & N_INDR) == 0 ) + externalNames.insert(symName); } // verify no undefines with same name as an external symbol const macho_nlist<P>* const undefinesStart = &fSymbols[fDynamicSymbolTable->iundefsym()]; @@ -876,8 +929,128 @@ void MachOChecker<A>::checkRelocations() for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { this->checkLocalReloation(reloc); } + + // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs + const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command<P>* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command<P>::CMD ) { + const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd; + // if segment is writable, we are fine + if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) + continue; + // look at sections that have text reloc bit set + const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>)); + const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags() & S_ATTR_LOC_RELOC) != 0 ) { + if ( ! hasTextRelocInRange(sect->addr(), sect->addr()+sect->size()) ) { + throwf("section %s has attribute set that it has relocs, but it has none", sect->sectname()); + } + } + } + } + cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize()); + } } +template <typename A> +typename A::P::uint_t MachOChecker<A>::segStartAddress(uint8_t segIndex) +{ + if ( segIndex > fSegments.size() ) + throw "segment index out of range"; + return fSegments[segIndex]->vmaddr(); +} + +template <typename A> +bool MachOChecker<A>::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd) +{ + // look at local relocs + const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount]; + for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) { + pint_t relocAddress = reloc->r_address() + this->relocBase(); + if ( (rangeStart <= relocAddress) && (relocAddress < rangeEnd) ) + return true; + } + // look rebase info + if ( fDyldInfo != NULL ) { + const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->rebase_off(); + const uint8_t* end = &p[fDyldInfo->rebase_size()]; + + uint8_t type = 0; + uint64_t segOffset = 0; + uint32_t count; + uint32_t skip; + int segIndex; + pint_t segStartAddr = 0; + pint_t addr; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segStartAddr = segStartAddress(segIndex); + segOffset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + addr = segStartAddr+segOffset; + if ( (rangeStart <= addr) && (addr < rangeEnd) ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + addr = segStartAddr+segOffset; + if ( (rangeStart <= addr) && (addr < rangeEnd) ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + addr = segStartAddr+segOffset; + if ( (rangeStart <= addr) && (addr < rangeEnd) ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += read_uleb128(p, end) + sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + addr = segStartAddr+segOffset; + if ( (rangeStart <= addr) && (addr < rangeEnd) ) + return true; + //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName); + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad rebase opcode %d", *p); + } + } + } +} static void check(const char* path) { diff --git a/src/other/rebase.cpp b/src/other/rebase.cpp index f8dc1ee..c04fb0a 100644 --- a/src/other/rebase.cpp +++ b/src/other/rebase.cpp @@ -302,7 +302,6 @@ void Rebaser<A>::setBaseAddress(uint64_t addr) template <typename A> void Rebaser<A>::adjustLoadCommands() { - const macho_segment_command<P>* highestSegmentCmd = NULL; const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); const uint32_t cmd_count = fHeader->ncmds(); const macho_load_command<P>* cmd = cmds; @@ -317,6 +316,8 @@ void Rebaser<A>::adjustLoadCommands() break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { // clear expected timestamps so that this image will load with invalid prebinding macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd; @@ -326,7 +327,7 @@ void Rebaser<A>::adjustLoadCommands() case macho_routines_command<P>::CMD: // update -init command { - struct macho_routines_command<P>* routines = (struct macho_routines_command<P>*)cmd; + macho_routines_command<P>* routines = (macho_routines_command<P>*)cmd; routines->set_init_address(routines->init_address() + fSlide); } break; @@ -374,6 +375,7 @@ void Rebaser<A>::adjustSymbolTable() { const macho_dysymtab_command<P>* dysymtab = NULL; macho_nlist<P>* symbolTable = NULL; + const char* strings = NULL; // get symbol table info const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>)); @@ -385,7 +387,8 @@ void Rebaser<A>::adjustSymbolTable() { const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd; symbolTable = (macho_nlist<P>*)(((uint8_t*)fHeader) + symtab->symoff()); - } + strings = (char*)(((uint8_t*)fHeader) + symtab->stroff()); + } break; case LC_DYSYMTAB: dysymtab = (macho_dysymtab_command<P>*)cmd; @@ -404,8 +407,23 @@ void Rebaser<A>::adjustSymbolTable() // walk all local symbols and slide their n_value macho_nlist<P>* lastLocal = &symbolTable[dysymtab->ilocalsym()+dysymtab->nlocalsym()]; for (macho_nlist<P>* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { - if ( entry->n_sect() != NO_SECT ) + if ( ((entry->n_type() & N_STAB) == 0) && ((entry->n_type() & N_TYPE) == N_SECT) ) { entry->set_n_value(entry->n_value() + fSlide); + } + else if ( entry->n_type() & N_STAB ) { + // some stabs need to be slid too + switch ( entry->n_type() ) { + case N_FUN: + // don't slide end-of-function FUN which is FUN with no string + if ( (entry->n_strx() == 0) || (strings[entry->n_strx()] == '\0') ) + break; + case N_BNSYM: + case N_STSYM: + case N_LCSYM: + entry->set_n_value(entry->n_value() + fSlide); + break; + } + } } // FIXME ¥¥¥ adjust dylib_module if it exists @@ -448,7 +466,7 @@ void Rebaser<A>::rebaseAt(int segIndex, uint64_t offset, uint8_t type) if ( segIndex == segCount ) { const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd; lastSegMappedStart = (uint8_t*)fHeader + seg->fileoff(); - lastSegIndex == segCount; + lastSegIndex = segCount; break; } ++segCount; diff --git a/src/other/unwinddump.cpp b/src/other/unwinddump.cpp index 098d932..81e677d 100644 --- a/src/other/unwinddump.cpp +++ b/src/other/unwinddump.cpp @@ -59,8 +59,10 @@ class UnwindPrinter { public: static bool validFile(const uint8_t* fileContent); - static UnwindPrinter<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path) - { return new UnwindPrinter<A>(fileContent, fileLength, path); } + static UnwindPrinter<A>* make(const uint8_t* fileContent, uint32_t fileLength, + const char* path, bool showFunctionNames) + { return new UnwindPrinter<A>(fileContent, fileLength, + path, showFunctionNames); } virtual ~UnwindPrinter() {} @@ -77,9 +79,10 @@ private: typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet; - UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path); + UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, + const char* path, bool showFunctionNames); bool findUnwindSection(); - void printUnwindSection(); + void printUnwindSection(bool showFunctionNames); void getSymbolTableInfo(); const char* functionName(pint_t addr); static const char* archName(); @@ -195,7 +198,7 @@ bool UnwindPrinter<arm>::validFile(const uint8_t* fileContent) template <typename A> -UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path) +UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames) : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL), fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fMachHeaderAddress(0) { @@ -209,7 +212,7 @@ UnwindPrinter<A>::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, getSymbolTableInfo(); if ( findUnwindSection() ) - printUnwindSection(); + printUnwindSection(showFunctionNames); } @@ -664,7 +667,7 @@ void UnwindPrinter<A>::decode(uint32_t encoding, const uint8_t* funcStart, char* } template <typename A> -void UnwindPrinter<A>::printUnwindSection() +void UnwindPrinter<A>::printUnwindSection(bool showFunctionNames) { const uint8_t* sectionContent = (uint8_t*)fHeader + fUnwindSection->offset(); macho_unwind_info_section_header<P>* sectionHeader = (macho_unwind_info_section_header<P>*)(sectionContent); @@ -700,8 +703,8 @@ void UnwindPrinter<A>::printUnwindSection() printf("\tLSDA table: (section offset 0x%08X, count=%u)\n", lsdaIndexArraySectionOffset, lsdaIndexArrayCount); macho_unwind_info_section_header_lsda_index_entry<P>* lindex = (macho_unwind_info_section_header_lsda_index_entry<P>*)§ionContent[lsdaIndexArraySectionOffset]; for (uint32_t i=0; i < lsdaIndexArrayCount; ++i) { - printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", - i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), functionName(lindex[i].functionOffset()+fMachHeaderAddress)); + const char* name = showFunctionNames ? functionName(lindex[i].functionOffset()+fMachHeaderAddress) : ""; + printf("\t\t[%3u] funcOffset=0x%08X, lsdaOffset=0x%08X, %s\n", i, lindex[i].functionOffset(), lindex[i].lsdaOffset(), name); if ( *(((uint8_t*)fHeader) + lindex[i].lsdaOffset()) != 0xFF ) fprintf(stderr, "BAD LSDA entry (does not start with 0xFF) for %s\n", functionName(lindex[i].functionOffset()+fMachHeaderAddress)); } @@ -731,8 +734,9 @@ void UnwindPrinter<A>::printUnwindSection() } char encodingString[100]; decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString); + const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : ""; printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n", - j, funcOffset, entry[j].encoding(), encodingString, functionName(funcOffset+fMachHeaderAddress)); + j, funcOffset, entry[j].encoding(), encodingString, name); } } else if ( page->kind() == UNWIND_SECOND_LEVEL_COMPRESSED ) { @@ -755,7 +759,7 @@ void UnwindPrinter<A>::printUnwindSection() char encodingString[100]; uint32_t funcOff = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entries[j])+baseFunctionOffset; decode(encoding, ((const uint8_t*)fHeader)+funcOff, encodingString); - const char* name = functionName(funcOff+fMachHeaderAddress); + const char* name = showFunctionNames ? functionName(funcOff+fMachHeaderAddress) : ""; if ( encoding & UNWIND_HAS_LSDA ) { // verify there is a corresponding entry in lsda table bool found = false; @@ -780,7 +784,7 @@ void UnwindPrinter<A>::printUnwindSection() } -static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs) +static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs, bool showFunctionNames) { struct stat stat_buf; @@ -807,31 +811,31 @@ static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs) switch(cputype) { case CPU_TYPE_POWERPC: if ( UnwindPrinter<ppc>::validFile(p + offset) ) - UnwindPrinter<ppc>::make(p + offset, size, path); + UnwindPrinter<ppc>::make(p + offset, size, path, showFunctionNames); else throw "in universal file, ppc slice does not contain ppc mach-o"; break; case CPU_TYPE_I386: if ( UnwindPrinter<x86>::validFile(p + offset) ) - UnwindPrinter<x86>::make(p + offset, size, path); + UnwindPrinter<x86>::make(p + offset, size, path, showFunctionNames); else throw "in universal file, i386 slice does not contain i386 mach-o"; break; case CPU_TYPE_POWERPC64: if ( UnwindPrinter<ppc64>::validFile(p + offset) ) - UnwindPrinter<ppc64>::make(p + offset, size, path); + UnwindPrinter<ppc64>::make(p + offset, size, path, showFunctionNames); else throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; break; case CPU_TYPE_X86_64: if ( UnwindPrinter<x86_64>::validFile(p + offset) ) - UnwindPrinter<x86_64>::make(p + offset, size, path); + UnwindPrinter<x86_64>::make(p + offset, size, path, showFunctionNames); else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; case CPU_TYPE_ARM: if ( UnwindPrinter<arm>::validFile(p + offset) ) - UnwindPrinter<arm>::make(p + offset, size, path); + UnwindPrinter<arm>::make(p + offset, size, path, showFunctionNames); else throw "in universal file, arm slice does not contain arm mach-o"; break; @@ -842,19 +846,19 @@ static void dump(const char* path, const std::set<cpu_type_t>& onlyArchs) } } else if ( UnwindPrinter<x86>::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) { - UnwindPrinter<x86>::make(p, length, path); + UnwindPrinter<x86>::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter<ppc>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC) ) { - UnwindPrinter<ppc>::make(p, length, path); + UnwindPrinter<ppc>::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter<ppc64>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC64) ) { - UnwindPrinter<ppc64>::make(p, length, path); + UnwindPrinter<ppc64>::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter<x86_64>::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) { - UnwindPrinter<x86_64>::make(p, length, path); + UnwindPrinter<x86_64>::make(p, length, path, showFunctionNames); } else if ( UnwindPrinter<arm>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) { - UnwindPrinter<arm>::make(p, length, path); + UnwindPrinter<arm>::make(p, length, path, showFunctionNames); } else { throw "not a known file type"; @@ -870,6 +874,7 @@ int main(int argc, const char* argv[]) { std::set<cpu_type_t> onlyArchs; std::vector<const char*> files; + bool showFunctionNames = true; try { for(int i=1; i < argc; ++i) { @@ -890,6 +895,9 @@ int main(int argc, const char* argv[]) else throwf("unknown architecture %s", arch); } + else if ( strcmp(arg, "-no_symbols") == 0 ) { + showFunctionNames = false; + } else { throwf("unknown option: %s\n", arg); } @@ -910,7 +918,7 @@ int main(int argc, const char* argv[]) // process each file for(std::vector<const char*>::iterator it=files.begin(); it != files.end(); ++it) { - dump(*it, onlyArchs); + dump(*it, onlyArchs, showFunctionNames); } } diff --git a/unit-tests/bin/result-filter.pl b/unit-tests/bin/result-filter.pl index 8168e79..9c92392 100755 --- a/unit-tests/bin/result-filter.pl +++ b/unit-tests/bin/result-filter.pl @@ -93,6 +93,15 @@ sub process_entry { printf "%-40s FAIL Makefile failure\n", $test_name; $total_count++; + #my $line1; + #foreach $line1 (@{$$tbl{stdout}}) + #{ + # printf "stdout: %s\n", $line1; + #} + #foreach $line1 (@{$$tbl{stderr}}) + #{ + # printf "stderr: %s\n", $line1; + #} return; } @@ -127,5 +136,14 @@ sub process_entry { printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; $total_count++; + #my $line1; + #foreach $line1 (@{$$tbl{stdout}}) + #{ + # printf "stdout: %s\n", $line1; + #} + #foreach $line1 (@{$$tbl{stderr}}) + #{ + # printf "stderr: %s\n", $line1; + #} } } diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 7222efa..642e491 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -6,7 +6,7 @@ SHELL = /bin/sh ARCH ?= $(shell arch) # set default to be all -VALID_ARCHS ?= "ppc ppc64 i386 x86_64 armv6" +VALID_ARCHS ?= "i386 x86_64 armv6" MYDIR=$(shell cd ../../bin;pwd) LD = ld @@ -14,6 +14,7 @@ OBJECTDUMP = ObjectDump MACHOCHECK = machocheck OTOOL = otool REBASE = rebase +DYLDINFO = dyldinfo ifdef BUILT_PRODUCTS_DIR # if run within Xcode, add the just built tools to the command path @@ -23,17 +24,22 @@ ifdef BUILT_PRODUCTS_DIR OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck REBASE = ${BUILT_PRODUCTS_DIR}/rebase + UNWINDDUMP = ${BUILT_PRODUCTS_DIR}/unwinddump + DYLDINFO = ${BUILT_PRODUCTS_DIR}/dyldinfo else ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" "" # if run from Terminal inside unit-test directory RELEASEDIR=$(shell cd ../../../build/Release;pwd) + RELEASEADIR=$(shell cd ../../../build/Release-assert;pwd) DEBUGDIR=$(shell cd ../../../build/Debug;pwd) - PATH := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH} - COMPILER_PATH := ${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH} - LD = ${RELEASEDIR}/ld - OBJECTDUMP = ${RELEASEDIR}/ObjectDump - MACHOCHECK = ${RELEASEDIR}/machocheck - REBASE = ${RELEASEDIR}/rebase + PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH} + COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH} + LD = ${DEBUGDIR}/ld + OBJECTDUMP = ${DEBUGDIR}/ObjectDump + MACHOCHECK = ${DEBUGDIR}/machocheck + REBASE = ${DEBUGDIR}/rebase + UNWINDDUMP = ${DEBUGDIR}/unwinddump + DYLDINFO = ${DEBUGDIR}/dyldinfo else PATH := ${MYDIR}:${PATH}: COMPILER_PATH := ${MYDIR}:${COMPILER_PATH}: @@ -42,12 +48,15 @@ endif export PATH export COMPILER_PATH +ifeq ($(ARCH),ppc) + SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk +endif -CC = gcc-4.2 -arch ${ARCH} ${SDKExtra} +CC = cc -arch ${ARCH} ${SDKExtra} CCFLAGS = -Wall -std=c99 ASMFLAGS = -CXX = g++-4.2 -arch ${ARCH} ${SDKExtra} +CXX = c++ -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall ifeq ($(ARCH),armv6) @@ -89,6 +98,7 @@ else FILEARCH = $(ARCH) endif + RM = rm RMFLAGS = -rf diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index 842ce77..f6275a7 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -6,6 +6,7 @@ unset LD_TRACE_DYLIBS unset LD_TRACE_ARCHIVES export DYLD_FALLBACK_LIBRARY_PATH=${DYLD_FALLBACK_LIBRARY_PATH}:/Developer/usr/lib +export MACOSX_DEPLOYMENT_TARGET=10.7 # cd into test-cases directory cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` diff --git a/unit-tests/test-cases/16-byte-alignment/Makefile b/unit-tests/test-cases/16-byte-alignment/Makefile index a3a256f..e178ddb 100644 --- a/unit-tests/test-cases/16-byte-alignment/Makefile +++ b/unit-tests/test-cases/16-byte-alignment/Makefile @@ -33,7 +33,7 @@ all: ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -c -O2 tl_test2.c -o tl_test2-${ARCH}.o # verify that the alignment is correct in the .o - ObjectDump -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null + ${OBJECTDUMP} -only _ai -align -no_content tl_test2-${ARCH}.o|${FAIL_IF_ERROR} grep '\<0 mod 16\>' >/dev/null # now verify the executable ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} -arch ${ARCH} -O2 tl_test2-${ARCH}.o -o tl_test2-${ARCH} diff --git a/unit-tests/test-cases/Lpath/Makefile b/unit-tests/test-cases/Lpath/Makefile new file mode 100644 index 0000000..b580706 --- /dev/null +++ b/unit-tests/test-cases/Lpath/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + mkdir -p hide + libtool foo.o -static -o hide/libfoo.a + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} main.o -lfoo -Lhide -r -o mainfoo.o + ${LD} main.o -lfoo -L hide -r -o mainfoo.o + ${PASS_IFF} true + +clean: + rm -rf foo.o hide main.o mainfoo.o diff --git a/unit-tests/test-cases/Lpath/foo.c b/unit-tests/test-cases/Lpath/foo.c new file mode 100644 index 0000000..8c5841a --- /dev/null +++ b/unit-tests/test-cases/Lpath/foo.c @@ -0,0 +1,2 @@ + +int foo() { return 0; } diff --git a/unit-tests/test-cases/Lpath/main.c b/unit-tests/test-cases/Lpath/main.c new file mode 100644 index 0000000..49deebf --- /dev/null +++ b/unit-tests/test-cases/Lpath/main.c @@ -0,0 +1,8 @@ + +extern int foo(); + +int main() +{ + foo(); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/absolute-symbol/abs.s b/unit-tests/test-cases/absolute-symbol/abs.s index 216867c..1aee96f 100644 --- a/unit-tests/test-cases/absolute-symbol/abs.s +++ b/unit-tests/test-cases/absolute-symbol/abs.s @@ -1,3 +1,7 @@ .globl _myAbs - .set _myAbs, 0xfe000000 +#if __LP64__ + _myAbs = 0x012345678 +#else + _myAbs = 0xfe000000 +#endif diff --git a/unit-tests/test-cases/alias-command-line/Makefile b/unit-tests/test-cases/alias-command-line/Makefile index 9e87329..f700dbf 100644 --- a/unit-tests/test-cases/alias-command-line/Makefile +++ b/unit-tests/test-cases/alias-command-line/Makefile @@ -41,11 +41,11 @@ all: ${CC} ${ASMFLAGS} aliases.s -c -o aliases.tmp.${ARCH}.o ${FAIL_IF_BAD_OBJ} aliases.tmp.${ARCH}.o - ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias _foo _fooalt -alias _foo _fooalt2 -o aliases.cmdline.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.tmp.${ARCH}.o -alias _foo _fooalt -alias _foo _fooalt2 -alias _hidden _glob -o aliases.cmdline.${ARCH}.o ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.cmdline.${ARCH}.o > aliases.cmdline.${ARCH}.o.dump ${FAIL_IF_ERROR} diff aliases.source.${ARCH}.o.dump aliases.cmdline.${ARCH}.o.dump - ${LD} -arch ${ARCH} -r aliases.tmp.${ARCH}.o -alias_list aliases.txt -o aliases.file.${ARCH}.o + ${LD} -arch ${ARCH} -r -keep_private_externs aliases.tmp.${ARCH}.o -alias_list aliases.txt -o aliases.file.${ARCH}.o ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content -no_sort aliases.file.${ARCH}.o > aliases.file.${ARCH}.o.dump ${PASS_IFF} diff aliases.source.${ARCH}.o.dump aliases.file.${ARCH}.o.dump diff --git a/unit-tests/test-cases/alias-command-line/aliases.s b/unit-tests/test-cases/alias-command-line/aliases.s index ffab4a9..48e5961 100644 --- a/unit-tests/test-cases/alias-command-line/aliases.s +++ b/unit-tests/test-cases/alias-command-line/aliases.s @@ -29,13 +29,20 @@ _temp: nop .globl _foo _foo: nop nop + + .globl _hidden + .private_extern _hidden +_hidden: nop + nop #if ALIASES .globl _fooalt .globl _fooalt2 + .globl _glob /* this should make an alias "_fooalt" for "_foo" */ _fooalt = _foo _fooalt2 = _foo +_glob = _hidden #endif _bar: nop diff --git a/unit-tests/test-cases/alias-command-line/aliases.txt b/unit-tests/test-cases/alias-command-line/aliases.txt index 291f2f7..d0a3da9 100644 --- a/unit-tests/test-cases/alias-command-line/aliases.txt +++ b/unit-tests/test-cases/alias-command-line/aliases.txt @@ -2,5 +2,6 @@ _foo _fooalt # comment _foo _fooalt2 +_hidden _glob diff --git a/unit-tests/test-cases/allow_heap_execute/Makefile b/unit-tests/test-cases/allow_heap_execute/Makefile new file mode 100644 index 0000000..57c3da7 --- /dev/null +++ b/unit-tests/test-cases/allow_heap_execute/Makefile @@ -0,0 +1,35 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the we set the data execute bit for i386 +# + +run: run-${ARCH} + +run-x86_64: + ${PASS_IFF} true + +run-armv6: + ${PASS_IFF} true + +run-armv7: + ${PASS_IFF} true + +run-ppc: + ${PASS_IFF} true + + +run-i386: + # Test with the flag + ${CC} ${CCFLAGS} main.c -o main-allow -Wl,-allow_heap_execute + ${FAIL_IF_BAD_MACHO} main-allow + ${OTOOL} -hv main-allow | grep MH_NO_HEAP_EXECUTION | ${FAIL_IF_STDIN} + # Test without the flag + ${CC} ${CCFLAGS} main.c -o main + ${OTOOL} -hv main | grep MH_NO_HEAP_EXECUTION | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main main-allow diff --git a/unit-tests/test-cases/allow_heap_execute/main.c b/unit-tests/test-cases/allow_heap_execute/main.c new file mode 100644 index 0000000..e0d6d5d --- /dev/null +++ b/unit-tests/test-cases/allow_heap_execute/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/unit-tests/test-cases/archive-ObjC-unexported/Makefile b/unit-tests/test-cases/archive-ObjC-unexported/Makefile new file mode 100644 index 0000000..f94ee81 --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC-unexported/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# The point of this test is to check that -ObjC loads all (and only) +# .o files that contain Objective-C code. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -c -o foo.o + ${CC} ${CCFLAGS} bar.m -c -o bar.o + libtool -static bar.o foo.o -o libfoo.a + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.m libfoo.a -o main -framework Foundation -dead_strip -Wl,-unexported_symbols_list,main.nexp 2>main.log + grep Foobar main.log | ${FAIL_IF_EMPTY} + ${PASS_IFF_SUCCESS} true + +clean: + rm -rf foo.o bar.o libfoo.a main.log diff --git a/unit-tests/test-cases/archive-ObjC-unexported/bar.m b/unit-tests/test-cases/archive-ObjC-unexported/bar.m new file mode 100644 index 0000000..ad598bc --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC-unexported/bar.m @@ -0,0 +1,14 @@ +#include <Foundation/Foundation.h> + +@interface Foobar : NSObject +@end + +void other() +{ + [[Foobar alloc] init]; +} + +void bar() +{ +} + diff --git a/unit-tests/test-cases/archive-ObjC-unexported/foo.m b/unit-tests/test-cases/archive-ObjC-unexported/foo.m new file mode 100644 index 0000000..acba7a4 --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC-unexported/foo.m @@ -0,0 +1,8 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +@end + +@implementation Foo +@end + diff --git a/unit-tests/test-cases/archive-ObjC-unexported/main.m b/unit-tests/test-cases/archive-ObjC-unexported/main.m new file mode 100644 index 0000000..58a039a --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC-unexported/main.m @@ -0,0 +1,9 @@ +#include <stdio.h> + +extern void bar(); + +int main() +{ + bar(); + return 0; +} diff --git a/unit-tests/test-cases/archive-ObjC-unexported/main.nexp b/unit-tests/test-cases/archive-ObjC-unexported/main.nexp new file mode 100644 index 0000000..0b864aa --- /dev/null +++ b/unit-tests/test-cases/archive-ObjC-unexported/main.nexp @@ -0,0 +1 @@ +_bar diff --git a/unit-tests/test-cases/archive-image_info/Makefile b/unit-tests/test-cases/archive-image_info/Makefile new file mode 100644 index 0000000..5bb026f --- /dev/null +++ b/unit-tests/test-cases/archive-image_info/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that objc files loaded from archives set image info +# + +IMAGE_INFO = "__image_info" + +ifeq ($(ARCH),x86_64) + IMAGE_INFO = "__objc_imageinfo" +endif +ifeq ($(ARCH),armv6) + IMAGE_INFO = "__objc_imageinfo" +endif + + +run: all + +all: + ${CC} ${CCFLAGS} main.m -c -o main.o + libtool -static main.o -o libmain.a + ${CC} ${CCFLAGS} libmain.a -o main -framework Foundation + size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main.o libmain.a main diff --git a/unit-tests/test-cases/archive-image_info/main.m b/unit-tests/test-cases/archive-image_info/main.m new file mode 100644 index 0000000..b012f44 --- /dev/null +++ b/unit-tests/test-cases/archive-image_info/main.m @@ -0,0 +1,36 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +@end + +@implementation Foo +@end + +int main() +{ + return 0; +} diff --git a/unit-tests/test-cases/bind_at_load/Makefile b/unit-tests/test-cases/bind_at_load/Makefile new file mode 100644 index 0000000..007feb5 --- /dev/null +++ b/unit-tests/test-cases/bind_at_load/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test -bind-at-load option +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib libbar.dylib -o main -Wl,-bind_at_load + dyldinfo -lazy_bind main | grep "no compressed lazy binding info" | ${FAIL_IF_EMPTY} + dyldinfo -bind main | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f libfoo.dylib libbar.dylib main diff --git a/unit-tests/test-cases/bind_at_load/bar.c b/unit-tests/test-cases/bind_at_load/bar.c new file mode 100644 index 0000000..d4a1007 --- /dev/null +++ b/unit-tests/test-cases/bind_at_load/bar.c @@ -0,0 +1,2 @@ + +void bar() {} diff --git a/unit-tests/test-cases/bind_at_load/foo.c b/unit-tests/test-cases/bind_at_load/foo.c new file mode 100644 index 0000000..ed41e9b --- /dev/null +++ b/unit-tests/test-cases/bind_at_load/foo.c @@ -0,0 +1,3 @@ + +void foo() {} +void foo2() {} diff --git a/unit-tests/test-cases/bind_at_load/main.c b/unit-tests/test-cases/bind_at_load/main.c new file mode 100644 index 0000000..7aadb5c --- /dev/null +++ b/unit-tests/test-cases/bind_at_load/main.c @@ -0,0 +1,14 @@ +#include <stddef.h> + +extern void foo(); +extern void foo2() __attribute__((weak_import)); +extern void bar(); + +int main() +{ + foo(); + bar(); + if ( &foo2 != NULL ) + foo2(); + return 0; +} diff --git a/unit-tests/test-cases/blank-stubs/Makefile b/unit-tests/test-cases/blank-stubs/Makefile index 346e2b7..19f0af1 100644 --- a/unit-tests/test-cases/blank-stubs/Makefile +++ b/unit-tests/test-cases/blank-stubs/Makefile @@ -23,7 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(VALID_ARCHS)) +ALL_ARCH_OPTIONS = $(patsubst %,-arch %,$(subst ppc,,$(VALID_ARCHS)) ) # # Test that blank stubs are handled properly @@ -34,7 +34,7 @@ run: all all: # build example fully fat dylib - gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib + gcc `echo ${ALL_ARCH_OPTIONS}` -dynamiclib foo.c -o libfoo.dylib -install_name libfoo.dylib ${FAIL_IF_BAD_MACHO} libfoo.dylib # handle the case of a native ppc compile--this sets the subtype, which must be passed to lipo diff --git a/unit-tests/test-cases/branch-distance/Makefile b/unit-tests/test-cases/branch-distance/Makefile index 46472ee..e2f7c30 100644 --- a/unit-tests/test-cases/branch-distance/Makefile +++ b/unit-tests/test-cases/branch-distance/Makefile @@ -35,9 +35,9 @@ run: all all: ${CC} ${CCFLAGS} foo.s -c ${CC} ${CCFLAGS} bar.s -c - ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar ${ARCH_FLAGS} -nostdlib + ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar ${ARCH_FLAGS} -nostdlib -lSystem ${LD} -arch ${ARCH} -r foo.o bar.o -o foobar.o - ${CC} ${CCFLAGS} foobar.o -e _foo -o foobar2 ${ARCH_FLAGS} -nostdlib + ${CC} ${CCFLAGS} foobar.o -e _foo -o foobar2 ${ARCH_FLAGS} -nostdlib -lSystem ${PASS_IFF_GOOD_MACHO} foobar clean: diff --git a/unit-tests/test-cases/branch-interworking/Makefile b/unit-tests/test-cases/branch-interworking/Makefile new file mode 100644 index 0000000..c83e25e --- /dev/null +++ b/unit-tests/test-cases/branch-interworking/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +# +# test thumb2 branch ranges +# + +run: all + + +all: + ${CC} ${CCFLAGS} mythumb.s -c + ${CC} ${CCFLAGS} myarm.s -c + ${CC} ${CCFLAGS} mythumb.o myarm.o -dynamiclib -o liball.dylib + ${PASS_IFF_GOOD_MACHO} liball.dylib + +clean: + rm *.o liball.dylib diff --git a/unit-tests/test-cases/branch-interworking/myarm.s b/unit-tests/test-cases/branch-interworking/myarm.s new file mode 100644 index 0000000..a3c7818 --- /dev/null +++ b/unit-tests/test-cases/branch-interworking/myarm.s @@ -0,0 +1,19 @@ + + .text +#if __arm__ + .code 32 + .align 2 +#endif + + .globl _myarm +_myarm: + nop +#if __arm__ + //bl _mythumb + b _mythumb +#elif __i386__ || __x86_64__ + jmp _mythumb +#endif + + + .subsections_via_symbols diff --git a/unit-tests/test-cases/branch-interworking/mythumb.s b/unit-tests/test-cases/branch-interworking/mythumb.s new file mode 100644 index 0000000..a58c28f --- /dev/null +++ b/unit-tests/test-cases/branch-interworking/mythumb.s @@ -0,0 +1,20 @@ + + .text +#if __arm__ + .syntax unified + .thumb_func _mythumb + .code 16 +#endif + + .globl _mythumb +_mythumb: + nop +#if __arm__ + //bl _myarm + b.w _myarm +#elif __i386__ || __x86_64__ + jmp _myarm +#endif + + + .subsections_via_symbols diff --git a/unit-tests/test-cases/cfstring-and-cstring/Makefile b/unit-tests/test-cases/cfstring-and-cstring/Makefile new file mode 100644 index 0000000..6f9b150 --- /dev/null +++ b/unit-tests/test-cases/cfstring-and-cstring/Makefile @@ -0,0 +1,14 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +run: all + +all: + ${CC} ${CCFLAGS} foo.c bar.c -Os -o foo -framework CoreFoundation + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf foo diff --git a/unit-tests/test-cases/cfstring-and-cstring/bar.c b/unit-tests/test-cases/cfstring-and-cstring/bar.c new file mode 100644 index 0000000..3441ce9 --- /dev/null +++ b/unit-tests/test-cases/cfstring-and-cstring/bar.c @@ -0,0 +1,14 @@ +#include <CoreFoundation/CFString.h> + + +// +// llvm may make cfstring that has backing store of kTest +// + + +const char kTest[] = "test"; + +void bar() +{ + CFStringGetLength(CFSTR("test")); +} diff --git a/unit-tests/test-cases/cfstring-and-cstring/foo.c b/unit-tests/test-cases/cfstring-and-cstring/foo.c new file mode 100644 index 0000000..86b0d6e --- /dev/null +++ b/unit-tests/test-cases/cfstring-and-cstring/foo.c @@ -0,0 +1,12 @@ +#include <CoreFoundation/CFString.h> + +extern void bar(); + +int main() +{ + CFStringGetLength(CFSTR("stuff")); + bar(); + return 0; +} + + diff --git a/unit-tests/test-cases/cfstring-utf16/Makefile b/unit-tests/test-cases/cfstring-utf16/Makefile index 3957151..272cb9f 100644 --- a/unit-tests/test-cases/cfstring-utf16/Makefile +++ b/unit-tests/test-cases/cfstring-utf16/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008-2009 Apple Inc. All rights reserved. +# Copyright (c) 2008-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -34,7 +34,7 @@ ifeq (,${findstring 64,$(ARCH)}) else CFSTRING_SIZE = 64 endif - USTRING_SIZE = 27 + USTRING_SIZE = 28 diff --git a/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile b/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile index 72f365b..cdfe6ac 100644 --- a/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile +++ b/unit-tests/test-cases/coalesce_weak_def_in_dylib/Makefile @@ -33,7 +33,7 @@ run: all-${ARCH} all-ppc: ${PASS_IFF} true -all-arm: +all-armv6: ${PASS_IFF} true all-i386: all-real diff --git a/unit-tests/test-cases/commons-order/Makefile b/unit-tests/test-cases/commons-order/Makefile index 68d809c..9c97867 100644 --- a/unit-tests/test-cases/commons-order/Makefile +++ b/unit-tests/test-cases/commons-order/Makefile @@ -31,7 +31,7 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} baz.c -fno-common -c -o baz.o - ${CC} ${CCFLAGS} main.c foo.c bar.c baz.o -o main -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} main.c foo.c bar.c baz.o -o main nm -j -n main | grep _common > symbol.order ${FAIL_IF_ERROR} diff symbol.order expected.order ${PASS_IFF_GOOD_MACHO} main diff --git a/unit-tests/test-cases/cstring-alt-segment/Makefile b/unit-tests/test-cases/cstring-alt-segment/Makefile index 4e55304..ff0919b 100644 --- a/unit-tests/test-cases/cstring-alt-segment/Makefile +++ b/unit-tests/test-cases/cstring-alt-segment/Makefile @@ -33,6 +33,7 @@ run: all all: ${CC} ${CCFLAGS} main.c custom.s -o main size -l main | grep __cstring | wc -l | grep 2 | ${FAIL_IF_EMPTY} + size -l main | grep -A2 __MYSEG | grep __cstring | grep " 10 " | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/cstring-alt-segment/custom.s b/unit-tests/test-cases/cstring-alt-segment/custom.s index 33705e5..8871b8a 100644 --- a/unit-tests/test-cases/cstring-alt-segment/custom.s +++ b/unit-tests/test-cases/cstring-alt-segment/custom.s @@ -2,7 +2,9 @@ .section __MYSEG, __cstring, cstring_literals -LC: .ascii "hello" +LC1: .ascii "hello\0" +LC2: .ascii "bye\0" +LC3: .ascii "hello\0" diff --git a/unit-tests/test-cases/cstring-empty-labeled/Makefile b/unit-tests/test-cases/cstring-empty-labeled/Makefile new file mode 100644 index 0000000..9c3504d --- /dev/null +++ b/unit-tests/test-cases/cstring-empty-labeled/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify labled empty strings are preserved +# +# + + +run: all + +all: + ${CC} ${CCFLAGS} -c foo.s -o foo.o + ${LD} -arch ${ARCH} -r foo.o -o foo-r.o + ${LD} -arch ${ARCH} -r foo-r.o -o foo-r2.o + ${OBJECTDUMP} foo-r.o > foo-r.dump + ${OBJECTDUMP} foo-r2.o > foo-r2.dump + ${PASS_IFF} diff foo-r.dump foo-r2.dump + +clean: + rm foo.o foo-r.o foo-r2.o foo.dump foo-r.dump foo-r2.dump diff --git a/unit-tests/test-cases/cstring-empty-labeled/foo.s b/unit-tests/test-cases/cstring-empty-labeled/foo.s new file mode 100644 index 0000000..9498b53 --- /dev/null +++ b/unit-tests/test-cases/cstring-empty-labeled/foo.s @@ -0,0 +1,21 @@ + .cstring +LC2: + .ascii "bye\0" + .ascii "\0" + .ascii "\0" + +.globl _empty +_empty: + .ascii "\0" + +LC0: + .ascii "hello\0" + .ascii "\0" + .ascii "\0" + .ascii "\0" + +LC1: + .ascii "\0" + + + .subsections_via_symbols diff --git a/unit-tests/test-cases/custom-segment-layout/Makefile b/unit-tests/test-cases/custom-segment-layout/Makefile new file mode 100644 index 0000000..1dfb23f --- /dev/null +++ b/unit-tests/test-cases/custom-segment-layout/Makefile @@ -0,0 +1,16 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that a large page zero works +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c zero.s -o main -pagezero_size 0 -segaddr __TEXT 0xb8000000 -segaddr __MYZEROPAGE 0 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main diff --git a/unit-tests/test-cases/custom-segment-layout/main.c b/unit-tests/test-cases/custom-segment-layout/main.c new file mode 100644 index 0000000..7f59a23 --- /dev/null +++ b/unit-tests/test-cases/custom-segment-layout/main.c @@ -0,0 +1,8 @@ + +int x = 5; + +int main() +{ + return 0; +} + diff --git a/unit-tests/test-cases/custom-segment-layout/zero.s b/unit-tests/test-cases/custom-segment-layout/zero.s new file mode 100644 index 0000000..86bde83 --- /dev/null +++ b/unit-tests/test-cases/custom-segment-layout/zero.s @@ -0,0 +1,11 @@ + + .section __MYZEROPAGE,_data +_min: .long 0 + + + .zerofill __MYZEROPAGE,__zerofill,_padding,2147483644 + + + + + diff --git a/unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile b/unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile new file mode 100644 index 0000000..59cadd4 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-duplicate-def/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that if an unused function is pulled in from an archive +# because another function in the same .o file is needed, that the +# first does not cause a duplicate definition error. +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static foo.o bar.o -o libfoobar.a + ${CC} ${CCFLAGS} main.c libfoobar.a -dead_strip -o main -Wl,-w + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main libfoobar.a foo.o bar.o diff --git a/unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c b/unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c new file mode 100644 index 0000000..87526e5 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-duplicate-def/bar.c @@ -0,0 +1,8 @@ + +void bar() { } + +int baz() +{ + return -1; +} + diff --git a/unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c b/unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c new file mode 100644 index 0000000..c8fcef2 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-duplicate-def/foo.c @@ -0,0 +1,10 @@ + +void foo() { } + +extern void bar(); + +void deadwood() +{ + bar(); +} + diff --git a/unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c b/unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c new file mode 100644 index 0000000..1f74deb --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-duplicate-def/main.c @@ -0,0 +1,14 @@ + +extern void foo(); + +int baz() +{ + return 0; +} + +int main() +{ + foo(); + return 0; +} + diff --git a/unit-tests/test-cases/dead_strip-archive-weak-override/Makefile b/unit-tests/test-cases/dead_strip-archive-weak-override/Makefile new file mode 100644 index 0000000..f4ed236 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-weak-override/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Tests that a non-weak symbol in an archive cleanly overrides +# a weak symbol in a .o file with dead code stripping +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static foo.o bar.o -o libfoobar.a + ${CC} ${CCFLAGS} main.c libfoobar.a -dead_strip -o main + nm main | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf main foo.o bar.o libfoobar.a diff --git a/unit-tests/test-cases/dead_strip-archive-weak-override/bar.c b/unit-tests/test-cases/dead_strip-archive-weak-override/bar.c new file mode 100644 index 0000000..605e44e --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-weak-override/bar.c @@ -0,0 +1,6 @@ + + +void bar() +{ +} + diff --git a/unit-tests/test-cases/dead_strip-archive-weak-override/foo.c b/unit-tests/test-cases/dead_strip-archive-weak-override/foo.c new file mode 100644 index 0000000..92bcb8f --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-weak-override/foo.c @@ -0,0 +1,15 @@ + +extern void bar(); + + +// strong definition of foo overrides weak definition +// in main.c, but this foo needs bar() +void foo() +{ + bar(); +} + +void loadme() +{ +} + diff --git a/unit-tests/test-cases/dead_strip-archive-weak-override/main.c b/unit-tests/test-cases/dead_strip-archive-weak-override/main.c new file mode 100644 index 0000000..81362f8 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-archive-weak-override/main.c @@ -0,0 +1,25 @@ + +extern void loadme(); + + +void bad() +{ +} + +// foo is first found be live here +// then the use of loadme causes libfoo.a(foo.o) +// to be loaded which overrides foo +__attribute__((weak)) void foo() +{ + bad(); +} + +int main() +{ + foo(); + loadme(); + foo(); + return 0; +} + + diff --git a/unit-tests/test-cases/dead_strip/Makefile b/unit-tests/test-cases/dead_strip/Makefile index 32ac1c5..a274694 100644 --- a/unit-tests/test-cases/dead_strip/Makefile +++ b/unit-tests/test-cases/dead_strip/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -35,6 +35,7 @@ run: all all: ${CC} ${CCFLAGS} main.c deadwood.c -dead_strip -o main-${ARCH} + size -l main-${ARCH} | grep __PAGEZERO | ${FAIL_IF_EMPTY} ${FAIL_IF_BAD_MACHO} main-${ARCH} nm -j main-${ARCH} | egrep 'dead_wood|dead_door' | ${FAIL_IF_STDIN} ${CC} ${CCFLAGS} -dynamiclib main.c deadwood.c -dead_strip -exported_symbols_list main.exp -o dylib-${ARCH} diff --git a/unit-tests/test-cases/demangle/Makefile b/unit-tests/test-cases/demangle/Makefile new file mode 100644 index 0000000..6ed52b7 --- /dev/null +++ b/unit-tests/test-cases/demangle/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Test that linker option -demangle works +# + +run: all + +all: + ${CXX} ${CXXFLAGS} main.cxx -c + ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o -lSystem -o main 2>main1.fail + grep __ZN3Foo4doitEv main1.fail | ${FAIL_IF_EMPTY} + ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o -lSystem -demangle -o main 2>main2.fail + grep 'Foo::doit()' main2.fail | ${PASS_IFF_STDIN} + + +clean: + rm -f main.o main1.fail main2.fail main diff --git a/unit-tests/test-cases/demangle/main.cxx b/unit-tests/test-cases/demangle/main.cxx new file mode 100644 index 0000000..af42ca6 --- /dev/null +++ b/unit-tests/test-cases/demangle/main.cxx @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <stdio.h> + +class Foo +{ +public: + Foo() {} + + void doit(); +}; + + + +int main() +{ + Foo f; + f.doit(); + + return 0; +} diff --git a/unit-tests/test-cases/dependency-logging/Makefile b/unit-tests/test-cases/dependency-logging/Makefile new file mode 100644 index 0000000..e861226 --- /dev/null +++ b/unit-tests/test-cases/dependency-logging/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that LD_TRACE_* work +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + rm -rf trace_ars + export LD_TRACE_ARCHIVES=1 && export LD_TRACE_FILE=trace_ars && ${CC} ${CCFLAGS} main.c libfoo.a -o main + grep 'libfoo.a' trace_ars | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + rm -rf trace_dylibs + export LD_TRACE_DYLIBS=1 && export LD_TRACE_FILE=trace_dylibs && ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + grep 'libfoo.dylib' trace_dylibs | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm -rf foo.o libfoo.a libfoo.dylib main trace_ars trace_dylibs diff --git a/unit-tests/test-cases/dependency-logging/foo.c b/unit-tests/test-cases/dependency-logging/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/unit-tests/test-cases/dependency-logging/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/unit-tests/test-cases/dependency-logging/main.c b/unit-tests/test-cases/dependency-logging/main.c new file mode 100644 index 0000000..a5a79d5 --- /dev/null +++ b/unit-tests/test-cases/dependency-logging/main.c @@ -0,0 +1,7 @@ +extern void foo(); + +int main() +{ + foo(); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/dtrace-old-probes/Makefile b/unit-tests/test-cases/dtrace-old-probes/Makefile new file mode 100644 index 0000000..61ddb05 --- /dev/null +++ b/unit-tests/test-cases/dtrace-old-probes/Makefile @@ -0,0 +1,36 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that old style _dtrace_probe$ labels are preserved +# + +all: + ${CC} ${CCFLAGS} main.c -o main + nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main diff --git a/unit-tests/test-cases/dtrace-old-probes/main.c b/unit-tests/test-cases/dtrace-old-probes/main.c new file mode 100644 index 0000000..811449a --- /dev/null +++ b/unit-tests/test-cases/dtrace-old-probes/main.c @@ -0,0 +1,49 @@ + +#include <stdio.h> + +#define DTRACE_STRINGIFY(s) #s +#define DTRACE_TOSTRING(s) DTRACE_STRINGIFY(s) + +#define DTRACE_NOPS \ + "nop" "\n\t" \ + "nop" "\n\t" \ + "nop" "\n\t" + + +#define DTRACE_LAB(p, n) \ + "__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n) + +#define DTRACE_LABEL(p, n) \ + ".section __DATA, __data\n\t" \ + ".globl " DTRACE_LAB(p, n) "\n\t" \ + DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \ + ".text" "\n\t" \ + "1:" + +#define DTRACE_CALL(p,n) \ + DTRACE_LABEL(p,n) \ + DTRACE_NOPS + +#define DTRACE_CALL0ARGS(provider, name) \ + __asm volatile ( \ + DTRACE_CALL(provider, name) \ + : \ + : \ + ); + +int deadwood() +{ + DTRACE_CALL0ARGS(__foo__, test2) + return 0; +} + + +int main() { + int a = 1; + + while(a) { + DTRACE_CALL0ARGS(__foo__, test1) + } + + return 0; +} diff --git a/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx b/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx index b756394..935ee12 100644 --- a/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx +++ b/unit-tests/test-cases/dtrace-static-probes-coalescing/x.cxx @@ -1,6 +1,6 @@ #include "header.h" -void f() { LOTS_O_PROBES } +void bar() { LOTS_O_PROBES } diff --git a/unit-tests/test-cases/dtrace-static-probes/Makefile b/unit-tests/test-cases/dtrace-static-probes/Makefile index b59760f..1688f23 100644 --- a/unit-tests/test-cases/dtrace-static-probes/Makefile +++ b/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -30,7 +30,7 @@ include ${TESTROOT}/include/common.makefile all: run -run: main main-r main-dead_strip libmain.dylib +run: main main-dead_strip libmain.dylib main-r ${FAIL_IF_BAD_MACHO} main ${PASS_IFF_GOOD_MACHO} main-r diff --git a/unit-tests/test-cases/dwarf-debug-notes-r/Makefile b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile index 1eb6310..9770686 100644 --- a/unit-tests/test-cases/dwarf-debug-notes-r/Makefile +++ b/unit-tests/test-cases/dwarf-debug-notes-r/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -33,27 +33,16 @@ include ${TESTROOT}/include/common.makefile run: all -all: foobar.o main.o - ${CXX} ${CCXXFLAGS} foobar.o main.o -o dwarf-test-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-test-${ARCH} - nm -ap dwarf-test-${ARCH} | ./stabs-filter.pl > dwarf-test-${ARCH}.stabs - ${PASS_IFF} diff dwarf-test-${ARCH}.stabs expected-stabs - -foobar.o : foo.o bar.o +all: + ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o foo.o -mdynamic-no-pic + ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o bar.o -mdynamic-no-pic ${LD} -r -arch ${ARCH} foo.o bar.o -o foobar.o - ${FAIL_IF_BAD_OBJ} foobar.o - -foo.o : foo.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 foo.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -bar.o : bar.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 bar.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -main.o : main.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ + ${CXX} ${CCXXFLAGS} -gdwarf-2 main.cxx -c -o main.o -mdynamic-no-pic + ${CXX} ${CCXXFLAGS} foobar.o main.o -o main + ${FAIL_IF_BAD_MACHO} main + nm -ap main | ./stabs-filter.pl > main.stabs + ${PASS_IFF} diff main.stabs expected-stabs + clean: - rm -rf dwarf-test-* *.o *.stabs + rm -rf foo.o bar.o main.o foobar.o main main.stabs diff --git a/unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile b/unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile new file mode 100644 index 0000000..e70c6b6 --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-uuid/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that UUID is the same for the two binaries built +# from the same source file but with different intermediate +# object file paths. +# + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 main.c -c -o main1.o + ${CC} ${CCFLAGS} -gdwarf-2 main.c -c -o main2.o + ${CC} ${CCFLAGS} main1.o -o main1 + ${CC} ${CCFLAGS} main2.o -o main2 + otool -lv main1 | grep -A3 UUID > main1.uuid + otool -lv main2 | grep -A3 UUID > main2.uuid + ${PASS_IFF} diff main1.uuid main2.uuid + + +clean: + rm -rf main1.o main2.o main1 main2 main1.uuid main2.uuid diff --git a/unit-tests/test-cases/dwarf-debug-notes-uuid/main.c b/unit-tests/test-cases/dwarf-debug-notes-uuid/main.c new file mode 100644 index 0000000..6ff8d4b --- /dev/null +++ b/unit-tests/test-cases/dwarf-debug-notes-uuid/main.c @@ -0,0 +1,23 @@ + + +void foo() +{ + +} + + +void bar() +{ + foo(); +} + + + +int main() +{ + bar(); + return 0; +} + + + diff --git a/unit-tests/test-cases/dwarf-debug-notes/Makefile b/unit-tests/test-cases/dwarf-debug-notes/Makefile index 42f1cb7..b4fa7ab 100644 --- a/unit-tests/test-cases/dwarf-debug-notes/Makefile +++ b/unit-tests/test-cases/dwarf-debug-notes/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -32,19 +32,13 @@ include ${TESTROOT}/include/common.makefile run: all -all: hello.o other.o - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o dwarf-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - nm -ap dwarf-hello-${ARCH} | ./stabs-filter.pl > dwarf-hello-${ARCH}.stabs - ${PASS_IFF} diff dwarf-hello-${ARCH}.stabs expected-stabs - -hello.o : hello.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ - -other.o : other.cxx - ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o $@ -mdynamic-no-pic - ${FAIL_IF_BAD_OBJ} $@ +all: + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o hello.o -mdynamic-no-pic + ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o other.o -mdynamic-no-pic + ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o hello + ${FAIL_IF_BAD_MACHO} hello + nm -ap hello | ./stabs-filter.pl > hello.stabs + ${PASS_IFF} diff hello.stabs expected-stabs clean: - rm -rf dwarf-hello-* *.o *.stabs + rm -rf hello hello.o other.o hello.stabs diff --git a/unit-tests/test-cases/dwarf-debug-notes/expected-stabs b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs index 0ef639d..95eb33d 100644 --- a/unit-tests/test-cases/dwarf-debug-notes/expected-stabs +++ b/unit-tests/test-cases/dwarf-debug-notes/expected-stabs @@ -7,7 +7,7 @@ 0000 ENSYM 0000 BNSYM 0000 FUN __Z3fooi -0000 SOL header.h +0000 SOL CWD//header.h 0000 FUN 0000 ENSYM 0000 SO @@ -22,12 +22,12 @@ 0000 FUN .my_non_standard_function_name 0000 FUN 0000 ENSYM -0000 GSYM _init 0000 STSYM .my_non_standard_name_static 0000 STSYM .my_non_standard_name 0000 STSYM __ZZ3bariE8bar_init +0000 GSYM _init 0000 GSYM _uninit +0000 STSYM __ZZ3bariE10bar_uninit 0000 STSYM __ZL7suninit 0000 STSYM __ZL5sinit -0000 STSYM __ZZ3bariE10bar_uninit 0000 SO diff --git a/unit-tests/test-cases/dwarf-strip-objc/Makefile b/unit-tests/test-cases/dwarf-strip-objc/Makefile new file mode 100644 index 0000000..3812933 --- /dev/null +++ b/unit-tests/test-cases/dwarf-strip-objc/Makefile @@ -0,0 +1,29 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that ld -r -S preserves the __objc_imageinfo section +# + +IMAGE_INFO = "__image_info" + +ifeq ($(ARCH),x86_64) + IMAGE_INFO = "__objc_imageinfo" +endif +ifeq ($(ARCH),armv6) + IMAGE_INFO = "__objc_imageinfo" +endif + + + +run: all + +all: + ${CC} ${CCFLAGS} -gdwarf-2 hello.m -c -o hello.o + ${LD} -r -S hello.o -o hello-r.o + size -l hello-r.o | grep ${IMAGE_INFO} | ${PASS_IFF_STDIN} + + + +clean: + rm -rf hello.o hello-r.o diff --git a/unit-tests/test-cases/dwarf-strip-objc/hello.m b/unit-tests/test-cases/dwarf-strip-objc/hello.m new file mode 100644 index 0000000..ffecb41 --- /dev/null +++ b/unit-tests/test-cases/dwarf-strip-objc/hello.m @@ -0,0 +1,8 @@ +#include <Foundation/Foundation.h> + + +int main() +{ + [NSString stringWithUTF8String: "hello"]; + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/dwarf-strip/Makefile b/unit-tests/test-cases/dwarf-strip/Makefile index 947afa7..8174f11 100644 --- a/unit-tests/test-cases/dwarf-strip/Makefile +++ b/unit-tests/test-cases/dwarf-strip/Makefile @@ -31,10 +31,10 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o dwarf-hello-${ARCH} - #${CC} ${CCFLAGS} -gdwarf-2 hello.c -o dwarf-hello-${ARCH} - ${FAIL_IF_BAD_MACHO} dwarf-hello-${ARCH} - nm -ap dwarf-hello-${ARCH} | grep -e " - " | ${PASS_IFF_EMPTY} + ${CC} ${CCFLAGS} -gdwarf-2 hello.c -Wl,-S -o hello + #${CC} ${CCFLAGS} -gdwarf-2 hello.c -o hello + ${FAIL_IF_BAD_MACHO} hello + nm -ap hello | grep -e " - " | ${PASS_IFF_EMPTY} clean: - rm -rf dwarf-hello-* + rm -rf hello hello.dSYM diff --git a/unit-tests/test-cases/dylib-aliases/Makefile b/unit-tests/test-cases/dylib-aliases/Makefile index 1cf9fa0..5ed2e91 100644 --- a/unit-tests/test-cases/dylib-aliases/Makefile +++ b/unit-tests/test-cases/dylib-aliases/Makefile @@ -31,7 +31,7 @@ include ${TESTROOT}/include/common.makefile run: all all: libfoo.dylib libbar.dylib libbaz.dylib - ${CC} ${CCFLAGS} main.c -o main -lfoo -lbar -lbaz -L. + ${CC} ${CCFLAGS} main.c -o main -lfoo -lbar -lbaz -L. -Wl,-w ${PASS_IFF_GOOD_MACHO} main libfoo.dylib : foo.c diff --git a/unit-tests/test-cases/dylib-re-export-cycle/Makefile b/unit-tests/test-cases/dylib-re-export-cycle/Makefile index 617fbfc..057ac03 100644 --- a/unit-tests/test-cases/dylib-re-export-cycle/Makefile +++ b/unit-tests/test-cases/dylib-re-export-cycle/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -32,19 +32,14 @@ SHELL = bash # use bash shell so we can redirect just stderr run: all -all: libfoo.dylib +all: + mkdir -p other + ${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4 + ${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4 + ${CC} foo.c -dynamiclib -o libfoo.dylib libbar.dylib -sub_library libbar -mmacosx-version-min=10.4 ${CC} main.c libfoo.dylib -o main -L. 2>errmsg || true grep "cycle" errmsg | ${PASS_IFF_STDIN} -libfoo.dylib : foo.c libbar.dylib - ${CC} foo.c -dynamiclib -o libfoo.dylib libbar.dylib -sub_library libbar -mmacosx-version-min=10.4 - -libbar.dylib : bar.c other/libfoo.dylib - ${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4 - -other/libfoo.dylib : foo.c - mkdir -p other - ${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4 diff --git a/unit-tests/test-cases/dylib-upward/Makefile b/unit-tests/test-cases/dylib-upward/Makefile new file mode 100644 index 0000000..5248a33 --- /dev/null +++ b/unit-tests/test-cases/dylib-upward/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# Verify upward dylib options work + +run: all + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-upward-lbar -L. + otool -lv libfoo.dylib | grep -A2 LC_LOAD_UPWARD_DYLIB | grep libbar.dylib | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-upward_library,libbar.dylib + otool -lv libfoo.dylib | grep -A2 LC_LOAD_UPWARD_DYLIB | grep libbar.dylib | ${FAIL_IF_EMPTY} + mkdir -p Bar.framework + ${CC} ${CCFLAGS} bar.c -dynamiclib -o Bar.framework/Bar + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-upward_framework,Bar -F. + otool -lv libfoo.dylib | grep -A2 LC_LOAD_UPWARD_DYLIB | grep Bar.framework/Bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf libfoo.dylib libbar.dylib Bar.framework diff --git a/unit-tests/test-cases/dylib-upward/bar.c b/unit-tests/test-cases/dylib-upward/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/unit-tests/test-cases/dylib-upward/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/unit-tests/test-cases/dylib-upward/foo.c b/unit-tests/test-cases/dylib-upward/foo.c new file mode 100644 index 0000000..bf74f09 --- /dev/null +++ b/unit-tests/test-cases/dylib-upward/foo.c @@ -0,0 +1,6 @@ +extern void bar(); + +void foo() +{ + bar(); +} diff --git a/unit-tests/test-cases/efi-basic/LibTest.c b/unit-tests/test-cases/efi-basic/LibTest.c new file mode 100644 index 0000000..de051c8 --- /dev/null +++ b/unit-tests/test-cases/efi-basic/LibTest.c @@ -0,0 +1,13 @@ + +char *gS = (void *)0; + +void LibInit(void) +{ +} + + +void OutputString(char *String) +{ + gS = String; +} + diff --git a/unit-tests/test-cases/efi-basic/Makefile b/unit-tests/test-cases/efi-basic/Makefile new file mode 100644 index 0000000..1f392c5 --- /dev/null +++ b/unit-tests/test-cases/efi-basic/Makefile @@ -0,0 +1,73 @@ +## +# Copyright (c) 2005-2010 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## + +# +# This test verifies ld -preload support required for mtoc +# to create a PE/COFF image that will work with EFI. +# The mach-o file and pecoff files are parsed with the +# test.py script to make sure the images look OK. +# +# Note: We should fail with an error condition if any of the all: +# commands fail. +# Note: Currently does not support ARM due to no ARM support in +# /usr/local/efi/bin/objdump +# +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +LOCAL_CC_FLAGS = -g -Os -fshort-wchar -fno-strict-aliasing -Wall -Werror -Wno-missing-braces + +#ARCH=x86_64 +#ARCH=i386 + + +ifeq (${ARCH},x86_64) + OBJ_DUMP_ARCH=efi-app-x86-64 + LD_ARG= +else + OBJ_DUMP_ARCH=efi-app-ia32 + LD_ARG=-read_only_relocs suppress +endif + + +# +# Compile the file +# + +all: + ${CC} -arch $(ARCH) -o LibTest1.obj $(LOCAL_CC_FLAGS) -c LibTest.c + libtool -static -o LibTest1.lib LibTest1.obj + ${CC} -arch $(ARCH) -o MtocTest1.obj $(LOCAL_CC_FLAGS) -c MtocTest.c + libtool -static -o MtocTest1.lib MtocTest1.obj + ld -arch $(ARCH) -o MtocTest1.macho -u __ModuleEntryPoint -e __ModuleEntryPoint $(LD_ARG) -preload -segalign 0x20 -pie -seg1addr 0x240 -map MtocTest1.map LibTest1.lib MtocTest1.lib + /usr/local/efi/bin/mtoc -subsystem application -align 0x20 -d MtocTest1.macho MtocTest1.macho MtocTest1.pecoff + otool -rR MtocTest1.macho > otool-reloc.log + otool -l MtocTest1.macho > otool-load.log + /usr/local/efi/bin/objdump -b $(OBJ_DUMP_ARCH) -x MtocTest1.pecoff > objdump-raw.log + ${PASS_IFF_SUCCESS} ./mtoctest.py --arch $(ARCH) + + +clean: + rm -rf *.pecoff *.macho *.obj *.log *.lib *.map + + diff --git a/unit-tests/test-cases/efi-basic/MtocTest.c b/unit-tests/test-cases/efi-basic/MtocTest.c new file mode 100644 index 0000000..5e3ebd5 --- /dev/null +++ b/unit-tests/test-cases/efi-basic/MtocTest.c @@ -0,0 +1,21 @@ + +extern void LibInit(void); + +extern void OutputString(char *String); + + + +char * gString = "\nHello world via mtoc - reloc\n"; + +int gWhyAreUninitializedGlobalsBad; + + +void _ModuleEntryPoint(void) +{ + __asm__ __volatile__ ("int $3"); + + LibInit (); + gWhyAreUninitializedGlobalsBad =1; + OutputString(gString); +} + diff --git a/unit-tests/test-cases/efi-basic/mtoctest.py b/unit-tests/test-cases/efi-basic/mtoctest.py new file mode 100755 index 0000000..460c8f5 --- /dev/null +++ b/unit-tests/test-cases/efi-basic/mtoctest.py @@ -0,0 +1,135 @@ +#!/usr/bin/python + +import sys +import getopt + +def Usage(): + print "Unkown command line args" + + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:],"hav:",["help","arch=","verbose"]) + except getopt.GetoptError: + print "Unkown command line args" + sys.exit(2) + + Verbose = 0 + for o,a in opts: + if o in ("-h", "--help"): + Usage () + sys.exit () + elif o in ("-a", "--arch"): + Arch = a + else: + Usage () + sys.exit () + + + if Verbose: + print "\nmach-o load commands:" + otoolload = open("otool-load.log", "r") + data = otoolload.read() + otoolload.close() + + + # extract extry point from ' ss 0x00000000 eflags 0x00000000 eip 0x00000259 cs 0x00000000' + if Arch == "i386": + eip = data.find("eip") + if eip != -1: + EntryPoint = int (data[eip + 4:eip + 4 + 10], 16) + + if Arch == "arm": + r15 = data.find("r15") + if r15 != -1: + EntryPoint = int (data[r15 + 4:r15 + 4 + 10], 16) + + # extract entry point from ' r15 0x0000000000000000 rip 0x0000000000000253' + if Arch == "x86_64": + rip = data.find("rip") + if rip != -1: + EntryPoint = int (data[rip + 4:rip + 4 + 18], 16) + + if EntryPoint == 0: + print "FAIL - no entry point for PE/COFF image" + sys.exit(-1) + else: + if Verbose: + print "Entry Point = 0x%08x" % EntryPoint + + + if Verbose: + print "\nPE/COFF dump:" + objdump = open("objdump-raw.log", "r") + data = objdump.read() + objdump.close() + + # Extract 'SizeOfImage 00000360' + Index = data.find("SizeOfImage") + End = data[Index:].find("\n") + SizeOfImage = int (data[Index+11:Index + End], 16); + if Verbose: + print "SizeOfImage = 0x%08x" % SizeOfImage + + #Parse ' 0 .text 00000080 00000240 00000240 00000240 2**2' + # ' CONTENTS, ALLOC, LOAD, READONLY, CODE ' + EndOfTable = data.find("SYMBOL TABLE:") + Index = data.find("Idx Name") + End = data[Index:].find("\n") + Index = Index + End + 1 + + PeCoffEnd = 0 + while Index < EndOfTable: + End = data[Index:].find("\n") + Split = data[Index:Index+End].split() + # Split[0] Indx + # Split[1] Name i.e. .text + # Split[2] Size + # Split[3] VMA + # Split[4] LMA + # Split[5] File Off + # Split[6] Align + if int(Split[3],16) != int(Split[5],16): + print "FAIL - %s VMA %08x not equal File off %08x XIP will not work" % (Split[1], int(Split[3],16), int(Split[5],16)) + sys.exit(-1) + + if int(Split[3],16) + int(Split[2],16) > PeCoffEnd: + PeCoffEnd = int(Split[3],16) + int(Split[2],16) + + if Split[1] == ".text": + SecStart = int(Split[3],16) + SecEnd = int(Split[3],16) + int(Split[2],16) + if (EntryPoint < SecStart) or (EntryPoint > SecEnd): + print "FAIL - Entry point (0x%x) not in .text section (0x%x - 0x%x)" % (EntryPoint, SecStart, SecEnd) + sys.exit(-1) + + if Verbose: + print "%10s" % Split[1] + ' ' + Split[2] + ' ' + Split[3] + ' ' + Split[4] + ' ' + Split[5] + " End = %x" % PeCoffEnd + Index += data[Index:].find("\n") + 1 + Index += data[Index:].find("\n") + 1 + + if SizeOfImage < PeCoffEnd: + print "FAIL - PE/COFF Header SizeOfImage (0x%x) is not correct. Image larger than size (0x%x)." % (SizeOfImage, PeCoffEnd) + sys.exit(-1) + + if Verbose: + print "\nmach-o relocations:" + otoolreloc = open("otool-reloc.log", "r") + lines = otoolreloc.readlines() + otoolreloc.close() + + found = False + for line in lines: + if found: + chunk = line.split() + if Verbose: + print chunk[0] + if line.find ("address") > -1: + found = True + + if Verbose: + print + + +if __name__ == "__main__": + main() diff --git a/unit-tests/test-cases/eh-coalescing/bar.cxx b/unit-tests/test-cases/eh-coalescing/bar.cxx index 83d1845..59aa60d 100644 --- a/unit-tests/test-cases/eh-coalescing/bar.cxx +++ b/unit-tests/test-cases/eh-coalescing/bar.cxx @@ -23,7 +23,7 @@ */ #include <stdio.h> -// this non-seak func() will have no unwind info and no LSDA +// this non-weak func() will have no unwind info and no LSDA int func() { return 0; } void foo(int x) {} diff --git a/unit-tests/test-cases/eh-stripped-symbols/Makefile b/unit-tests/test-cases/eh-stripped-symbols/Makefile index 107400e..15819f0 100644 --- a/unit-tests/test-cases/eh-stripped-symbols/Makefile +++ b/unit-tests/test-cases/eh-stripped-symbols/Makefile @@ -10,13 +10,13 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CXX} ${CCXXFLAGS} main.cxx -c -o main1.o -Os + ${CXX} ${CCXXFLAGS} main.cxx -g -c -o main1.o -Os #strip main1.o -u -s keep.exp -o main2.o ${LD} main1.o -r -x -exported_symbols_list keep.exp -o main2.o - ${CXX} ${CCXXFLAGS} main1.o -Wl,-x -o main1 -exported_symbols_list keep.exp + ${CXX} ${CCXXFLAGS} main1.o -o main1 ${CXX} ${CCXXFLAGS} main2.o -o main2 - unwinddump -arch ${ARCH} main1 > main1.unwind - unwinddump -arch ${ARCH} main2 > main2.unwind + ${UNWINDDUMP} -arch ${ARCH} -no_symbols main1 > main1.unwind + ${UNWINDDUMP} -arch ${ARCH} -no_symbols main2 > main2.unwind ${PASS_IFF} diff main1.unwind main2.unwind clean: diff --git a/unit-tests/test-cases/eh-stripped-symbols/main.cxx b/unit-tests/test-cases/eh-stripped-symbols/main.cxx index 325937d..90cb581 100644 --- a/unit-tests/test-cases/eh-stripped-symbols/main.cxx +++ b/unit-tests/test-cases/eh-stripped-symbols/main.cxx @@ -24,29 +24,48 @@ #include <stdio.h> #include <stdlib.h> -void bar() -{ -} - +int global = 0; -void foo2() -{ - int a = arc4random(); - int b = arc4random(); - fprintf(stderr, "hello %d %d\n", a, b); +int bar() +{ + global = 1; + throw 10; + } -void foo1() + +void foo() { - foo2(); - fprintf(stderr, "world\n"); + try { + bar(); + } + catch(int x) { + global = 2; + throw x; + } } int main() { - foo1(); - bar(); - return 0; -} \ No newline at end of file + int state = 1; + try { + state = 2; + foo(); + state = 3; + } + catch (int x) { + if ( state != 2 ) + return 1; + if ( x != 10 ) + return 1; + state = 4; + } + + if ( (state == 4) && (global == 2) ) + return 0; + else + return 1; +} + diff --git a/unit-tests/test-cases/empty-dylib/Makefile b/unit-tests/test-cases/empty-dylib/Makefile index a12e7c1..504fb97 100644 --- a/unit-tests/test-cases/empty-dylib/Makefile +++ b/unit-tests/test-cases/empty-dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2009 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -25,15 +25,23 @@ include ${TESTROOT}/include/common.makefile # # Check that the n_sect number for __mh_dylib_header is valid when there is no __text section +# Also check that codesigning works on empty dylib. # +CODESIGN_ARCH = ${ARCH} +ifeq (${ARCH},ppc) + CODESIGN_ARCH = ppc7400 +endif + + run: all all: ${CC} ${CCFLAGS} -dynamiclib justdata.c -o libjustdata.dylib -Wl,-no_compact_unwind ${PASS_IFF_GOOD_MACHO} libjustdata.dylib ${CC} ${CCFLAGS} -dynamiclib empty.c -o libempty.dylib -Wl,-no_compact_unwind - ${PASS_IFF_GOOD_MACHO} libempty.dylib + codesign_allocate -i libempty.dylib -a ${CODESIGN_ARCH} 1024 -o libsigned.dylib + ${PASS_IFF_GOOD_MACHO} libsigned.dylib clean: - rm -rf libempty.dylib libjustdata.dylib + rm -rf libempty.dylib libjustdata.dylib libsigned.dylib diff --git a/unit-tests/test-cases/exported-symbols-dead_strip/Makefile b/unit-tests/test-cases/exported-symbols-dead_strip/Makefile new file mode 100644 index 0000000..52c0d43 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-dead_strip/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Tests that link fails if undefined symbol is in export list +# + +run: all + +all: + ${PASS_IFF_ERROR} ${CC} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list foo.exp -dead_strip 2>/dev/null + +clean: + rm libfoo.dylib diff --git a/unit-tests/test-cases/exported-symbols-dead_strip/foo.c b/unit-tests/test-cases/exported-symbols-dead_strip/foo.c new file mode 100644 index 0000000..6bb8d95 --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-dead_strip/foo.c @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <stddef.h> + +void good() {} +void bad() {} + + +void ABC() {} +void ABD() { good(); } +void DEF() {} +void DEG() { bad(); } + diff --git a/unit-tests/test-cases/exported-symbols-dead_strip/foo.exp b/unit-tests/test-cases/exported-symbols-dead_strip/foo.exp new file mode 100644 index 0000000..565684e --- /dev/null +++ b/unit-tests/test-cases/exported-symbols-dead_strip/foo.exp @@ -0,0 +1,3 @@ +_ABC +_ABCD +_DEF diff --git a/unit-tests/test-cases/function-starts/Makefile b/unit-tests/test-cases/function-starts/Makefile new file mode 100644 index 0000000..0b43cd5 --- /dev/null +++ b/unit-tests/test-cases/function-starts/Makefile @@ -0,0 +1,29 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check -function_starts +# + +run: all + +all: + # as main executable + ${CC} ${CCFLAGS} main.c -o main -Wl,-function_starts + ${DYLDINFO} -function_starts main | grep _bar | ${FAIL_IF_EMPTY} + # as dylib + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib -Wl,-function_starts + ${DYLDINFO} -function_starts libmain.dylib | grep _bar | ${FAIL_IF_EMPTY} + # as dylib with prefered load address + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain2.dylib -seg1addr 0x200000 -Wl,-function_starts + ${DYLDINFO} -function_starts libmain2.dylib | grep _bar | ${FAIL_IF_EMPTY} + # as dylib with aliases + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain3.dylib -Wl,-function_starts -Wl,-alias,_mid,midalias + ${DYLDINFO} -function_starts libmain3.dylib | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + + +clean: + rm main libmain.dylib libmain2.dylib libmain3.dylib diff --git a/unit-tests/test-cases/function-starts/main.c b/unit-tests/test-cases/function-starts/main.c new file mode 100644 index 0000000..4ff1717 --- /dev/null +++ b/unit-tests/test-cases/function-starts/main.c @@ -0,0 +1,10 @@ + +void foo() {} + +void mid() {} + +static void bar() { foo(); } + +int main() { bar(); return 0; } + + diff --git a/unit-tests/test-cases/hidden-r/Makefile b/unit-tests/test-cases/hidden-r/Makefile new file mode 100644 index 0000000..702038c --- /dev/null +++ b/unit-tests/test-cases/hidden-r/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Tests that a hidden tentative or weak hidden symbol survives -r +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r main.o foo.o -o all.o + ${CC} ${CCFLAGS} all.o -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main.o foo.o all.o main diff --git a/unit-tests/test-cases/hidden-r/foo.c b/unit-tests/test-cases/hidden-r/foo.c new file mode 100644 index 0000000..8c03a7f --- /dev/null +++ b/unit-tests/test-cases/hidden-r/foo.c @@ -0,0 +1,5 @@ +void __attribute__((weak,visibility("hidden"))) my_weak() +{ +} + +int __attribute__((visibility("hidden"))) my_tent; diff --git a/unit-tests/test-cases/hidden-r/main.c b/unit-tests/test-cases/hidden-r/main.c new file mode 100644 index 0000000..352c2f8 --- /dev/null +++ b/unit-tests/test-cases/hidden-r/main.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +extern void my_weak(); +extern int my_tent; + +int main() +{ + my_tent = 0; + my_weak(); + return 0; +} + diff --git a/unit-tests/test-cases/kext-basic/mykext.c b/unit-tests/test-cases/kext-basic/mykext.c index 51de101..6dfeb5c 100644 --- a/unit-tests/test-cases/kext-basic/mykext.c +++ b/unit-tests/test-cases/kext-basic/mykext.c @@ -1,5 +1,6 @@ #include <mach/mach_types.h> +extern void extern_func(); int my_global = 3; extern int extern_global; @@ -7,6 +8,7 @@ extern int extern_global; kern_return_t mykext_start (kmod_info_t * ki, void * d) { ++my_global; ++extern_global; + extern_func(); return KERN_SUCCESS; } diff --git a/unit-tests/test-cases/label-on-end-of-section/Makefile b/unit-tests/test-cases/label-on-end-of-section/Makefile index a6f1537..bd9a8ff 100755 --- a/unit-tests/test-cases/label-on-end-of-section/Makefile +++ b/unit-tests/test-cases/label-on-end-of-section/Makefile @@ -31,7 +31,7 @@ run: all all: ${CC} ${CCFLAGS} foo.s -c -o foo.o - ${OBJECTDUMP} foo.o | grep "pointer to _end" | ${PASS_IFF_STDIN} + ${OBJECTDUMP} foo.o | grep "address of direct(_end)" | ${PASS_IFF_STDIN} clean: rm foo.o diff --git a/unit-tests/test-cases/large-bss/Makefile b/unit-tests/test-cases/large-bss/Makefile new file mode 100644 index 0000000..0e5a155 --- /dev/null +++ b/unit-tests/test-cases/large-bss/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld can link > 4GB zero fill section +# + + +run: all + +all: + ${CC} ${CCFLAGS} test.s -dynamiclib -o libtest.dylib + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib diff --git a/unit-tests/test-cases/large-bss/test.s b/unit-tests/test-cases/large-bss/test.s new file mode 100644 index 0000000..c09a83c --- /dev/null +++ b/unit-tests/test-cases/large-bss/test.s @@ -0,0 +1,33 @@ + +#if __x86_64__ + + +.lcomm _mediumarray1,4000,5 +.lcomm _bigarray1,2000000000,5 +.lcomm _bigarray2,2000000000,5 +.lcomm _bigarray3,2000000000,5 +.lcomm _small1,4,2 +.lcomm _small2,4,2 +.lcomm _small3,4,2 + + + .text +.globl _test +_test: + pushq %rbp + movq %rsp, %rbp + movq _bigarray1@GOTPCREL(%rip), %rax + movq _bigarray2@GOTPCREL(%rip), %rax + movq _bigarray3@GOTPCREL(%rip), %rax + leaq _small1(%rip),%rax + leaq _small2(%rip),%rax + leaq _small3(%rip),%rax + leaq _mediumarray1(%rip),%rax + leave + ret + + +#endif + + + diff --git a/unit-tests/test-cases/lazy-dylib-objc/Makefile b/unit-tests/test-cases/lazy-dylib-objc/Makefile index 975ad29..0eb717b 100644 --- a/unit-tests/test-cases/lazy-dylib-objc/Makefile +++ b/unit-tests/test-cases/lazy-dylib-objc/Makefile @@ -42,4 +42,4 @@ all: clean: - rm libfoo.dylib main rm fail.log + rm -f libfoo.dylib main rm fail.log diff --git a/unit-tests/test-cases/lazy-dylib/Makefile b/unit-tests/test-cases/lazy-dylib/Makefile index e3d0d6a..4f1b14d 100644 --- a/unit-tests/test-cases/lazy-dylib/Makefile +++ b/unit-tests/test-cases/lazy-dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# Copyright (c) 2008-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -35,10 +35,10 @@ run: all all: ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main + ${CC} ${CCFLAGS} main.c -lazy-lfoo -L. -o main ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad.c -Wl,-lazy_library,libfoo.dylib -o bad 2> fail.log ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bad2.c -Wl,-lazy_library,libfoo.dylib -o bad2 2> fail.log - ${PASS_IFF_GOOD_MACHO} main clean: diff --git a/unit-tests/test-cases/literals-labels/Makefile b/unit-tests/test-cases/literals-labels/Makefile new file mode 100644 index 0000000..92b3535 --- /dev/null +++ b/unit-tests/test-cases/literals-labels/Makefile @@ -0,0 +1,21 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Support mulitple labels on the same literal by cloning the literal +# into an atom per label. +# + +run: all + +all: + ${CC} ${ASMFLAGS} literals.s -c -o literals.o + ${OBJECTDUMP} literals.o > literals.o.dump + ${LD} -arch ${ARCH} -r literals.o -o literals-r.o + ${OBJECTDUMP} literals-r.o > literals-r.o.dump + ${PASS_IFF} diff literals.o.dump literals-r.o.dump + +clean: + rm -rf literals.o literals-r.o literals.o.dump literals-r.o.dump diff --git a/unit-tests/test-cases/literals-labels/literals.s b/unit-tests/test-cases/literals-labels/literals.s new file mode 100644 index 0000000..1fde088 --- /dev/null +++ b/unit-tests/test-cases/literals-labels/literals.s @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + .literal16 +L01:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654321 + +L02:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654322 + +_foo16: +_bar16: + .long 22345678 + .long 87654321 + .long 12345678 + .long 87654323 + +L04:.long 12345678 + .long 87654321 + .long 12345678 + .long 87654324 + + + .literal8 +L1: .long 12345678 + .long 87654321 + +L2: .long 12345678 + .long 87654322 + +_foo8: +_bar8: + .long 22345678 + .long 87654323 + +L4: .long 12345678 + .long 87654324 + + .literal4 +L11:.long 12345678 +L12:.long 12345679 +_foo4: +_bar4: + .long 22345670 +L14:.long 12345671 + + .cstring +L21: .ascii "hello\0" +L22: .ascii "hello,there\0" +_string1: +_string2: + .ascii "there\0" +L24: .ascii "bye\0" diff --git a/unit-tests/test-cases/llvm-integration/Makefile b/unit-tests/test-cases/llvm-integration/Makefile index 30376df..513dccc 100644 --- a/unit-tests/test-cases/llvm-integration/Makefile +++ b/unit-tests/test-cases/llvm-integration/Makefile @@ -251,16 +251,16 @@ seventeen: ${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o file ab17.o | grep "Mach-O" | ${PASS_IFF_EMPTY} # echo "verify ld -r of bitcode and mach-o produces mach-o" - ${LLVMGCC} ${CCFLAGS} b17.c -c -o b17.o - ${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o - file ab17.o | grep "Mach-O" | ${PASS_IFF_STDIN} + ${LLVMGCC} ${CCFLAGS} b17.c -c -o b17m.o + ${LD} -arch ${ARCH} -r a17.o b17m.o -o ab17m.o + file ab17m.o | grep "Mach-O" | ${PASS_IFF_STDIN} eighteen: #echo verify ld -r -keep_private_externs works ${LLVMGCC} ${CCFLAGS} --emit-llvm a18.c -c -o a18.o ${LD} -arch ${ARCH} -r -keep_private_externs a18.o -o a18-rkpe.o - ObjectDump -nm a18-rkpe.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} - ObjectDump -nm a18-rkpe.o | grep _func_hidden2 | grep " hidden" | ${FAIL_IF_EMPTY} + nm -nm a18-rkpe.o | grep _common_hidden1 | grep "private external" | ${FAIL_IF_EMPTY} + nm -nm a18-rkpe.o | grep _func_hidden2 | grep "private external" | ${FAIL_IF_EMPTY} #echo verify ld -r makes hidden symbols internal (except for commons) ${LD} -arch ${ARCH} -r a18.o -o a18-r.o #ObjectDump -nm a18-r.o | grep _common_hidden1 | grep " hidden" | ${FAIL_IF_EMPTY} diff --git a/unit-tests/test-cases/llvm-integration/a17.c b/unit-tests/test-cases/llvm-integration/a17.c index 3cd06fd..1b8d4bf 100644 --- a/unit-tests/test-cases/llvm-integration/a17.c +++ b/unit-tests/test-cases/llvm-integration/a17.c @@ -2,3 +2,11 @@ int a = 0; int func_a() { return a; } +// add code that will cause stack canary +extern void fill(char*); +void test() +{ + char buf[100]; + fill(buf); +} + diff --git a/unit-tests/test-cases/lto-dead_strip-objc/Makefile b/unit-tests/test-cases/lto-dead_strip-objc/Makefile new file mode 100644 index 0000000..ccd5b59 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-objc/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# <rdar://problem/8481987> Link Time Optimization crashes linker with 'dead code strip' + hidden symbol +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm foo.m -c -o foo.o -fvisibility=hidden + ${LLVMGCC} ${CCFLAGS} -dynamiclib foo.o -o libfoo.dylib -dead_strip -framework Foundation + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -f foo.o libfoo.dylib diff --git a/unit-tests/test-cases/lto-dead_strip-objc/foo.m b/unit-tests/test-cases/lto-dead_strip-objc/foo.m new file mode 100644 index 0000000..6ee5735 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-objc/foo.m @@ -0,0 +1,27 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +- (NSString*) foo; +@end + + +@implementation Foo +- (NSString*) foo +{ + return [NSString stringWithUTF8String:"hello"]; +} +@end + + +@interface Bar : NSData +- (NSArray*) bar; +@end + + +@implementation Bar +- (NSArray*) bar +{ + return [NSArray array]; +} +@end + diff --git a/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile b/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile new file mode 100644 index 0000000..d563c92 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-some-hidden/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# <rdar://problem/8481987> Link Time Optimization crashes linker with 'dead code strip' + hidden symbol +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o -fvisibility=hidden + ${LLVMGCC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip + ${PASS_IFF_GOOD_MACHO} libbar.dylib + +clean: + rm bar.o libbar.dylib diff --git a/unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c b/unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c new file mode 100644 index 0000000..480ac85 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-some-hidden/bar.c @@ -0,0 +1,19 @@ + +int data[] = { 4, 5, 6 }; +int deaddata[] = { 7, 8, 9 }; + +int func() { return 0; } +int deadfunc() { return 0; } + +__attribute__((visibility("default"))) +int* foo() +{ + return data; +} + +__attribute__((visibility("default"))) +void* foo2() +{ + return func; +} + diff --git a/unit-tests/test-cases/lto-dead_strip-tentative/Makefile b/unit-tests/test-cases/lto-dead_strip-tentative/Makefile new file mode 100644 index 0000000..fa0a02b --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-tentative/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# <rdar://problem/8708091> Link Time Optimization error with tentative defs and -dead_strip +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm foo.c -c -o foo.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm baz.c -c -o baz.o + ${LLVMGCC} ${CCFLAGS} main.c -c -o main.o + ${LLVMGCC} ${CCFLAGS} main.o foo.o bar.o baz.o -o main -dead_strip + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf foo.o bar.o baz.o main.o main diff --git a/unit-tests/test-cases/lto-dead_strip-tentative/bar.c b/unit-tests/test-cases/lto-dead_strip-tentative/bar.c new file mode 100644 index 0000000..d8fb526 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-tentative/bar.c @@ -0,0 +1,4 @@ + +int tent; + +int bar() { return tent; } diff --git a/unit-tests/test-cases/lto-dead_strip-tentative/baz.c b/unit-tests/test-cases/lto-dead_strip-tentative/baz.c new file mode 100644 index 0000000..d1cca6e --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-tentative/baz.c @@ -0,0 +1,5 @@ + +int tent; + +int baz() { return tent; } + diff --git a/unit-tests/test-cases/lto-dead_strip-tentative/foo.c b/unit-tests/test-cases/lto-dead_strip-tentative/foo.c new file mode 100644 index 0000000..983fe9c --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-tentative/foo.c @@ -0,0 +1,4 @@ + +int tent; + +int foo() { return tent; } diff --git a/unit-tests/test-cases/lto-dead_strip-tentative/main.c b/unit-tests/test-cases/lto-dead_strip-tentative/main.c new file mode 100644 index 0000000..4138be7 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-tentative/main.c @@ -0,0 +1,12 @@ + +extern int foo(); +extern int bar(); +extern int baz(); + +int main() +{ + foo(); + bar(); + baz(); + return 0; +} diff --git a/unit-tests/test-cases/lto-dead_strip-unused/Makefile b/unit-tests/test-cases/lto-dead_strip-unused/Makefile new file mode 100644 index 0000000..a518d82 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-unused/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# <rdar://problem/7438246> LTO with 'dead code strip' can't ignore unused functions with undefined references +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o + ${LLVMGCC} ${CCFLAGS} -dead_strip main.o bar.o -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f bar.o main.o main + diff --git a/unit-tests/test-cases/lto-dead_strip-unused/bar.c b/unit-tests/test-cases/lto-dead_strip-unused/bar.c new file mode 100644 index 0000000..f5f5823 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-unused/bar.c @@ -0,0 +1,15 @@ + + +void bar() +{ +} + + +extern void unused1(); + +void unused2() +{ + unused1(); +} + + diff --git a/unit-tests/test-cases/lto-dead_strip-unused/main.c b/unit-tests/test-cases/lto-dead_strip-unused/main.c new file mode 100644 index 0000000..f04d475 --- /dev/null +++ b/unit-tests/test-cases/lto-dead_strip-unused/main.c @@ -0,0 +1,8 @@ +extern void bar(); + +int main() +{ + bar(); + return 0; +} + diff --git a/unit-tests/test-cases/lto-objc-image-info/Makefile b/unit-tests/test-cases/lto-objc-image-info/Makefile new file mode 100644 index 0000000..8b13444 --- /dev/null +++ b/unit-tests/test-cases/lto-objc-image-info/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# <rdar://problem/8198537> trivial Objective-C app fails when using libLTO +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} +LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH} + + +IMAGE_INFO = "__image_info" + +ifeq ($(ARCH),x86_64) + IMAGE_INFO = "__objc_imageinfo" +endif +ifeq ($(ARCH),armv6) + IMAGE_INFO = "__objc_imageinfo" +endif + + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.m -c -o main.o + ${LLVMGCC} ${CCFLAGS} main.o -o main -framework Foundation + size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main main.o diff --git a/unit-tests/test-cases/lto-objc-image-info/main.m b/unit-tests/test-cases/lto-objc-image-info/main.m new file mode 100644 index 0000000..7b88000 --- /dev/null +++ b/unit-tests/test-cases/lto-objc-image-info/main.m @@ -0,0 +1,9 @@ + +#include <Foundation/Foundation.h> + +int main() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [pool drain]; + return 0; +} + diff --git a/unit-tests/test-cases/lto-object_path/Makefile b/unit-tests/test-cases/lto-object_path/Makefile new file mode 100644 index 0000000..aeda85d --- /dev/null +++ b/unit-tests/test-cases/lto-object_path/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that linker option -object_path_lto results in tmp .o file +# being produced. +# + +LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH} + +run: all + +all: + ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o -fvisibility=hidden + ${LLVMGCC} ${CCFLAGS} main.o -o main -Wl,-object_path_lto,`pwd`/main.tmp.o + nm main.tmp.o | grep _main | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main main.o main.tmp.o main diff --git a/unit-tests/test-cases/lto-object_path/main.c b/unit-tests/test-cases/lto-object_path/main.c new file mode 100644 index 0000000..578d24b --- /dev/null +++ b/unit-tests/test-cases/lto-object_path/main.c @@ -0,0 +1,15 @@ + +#include <stdio.h> + + +void foo(int x) +{ + printf("hello, world %d\n", x); +} + +int main() +{ + foo(10); + return 0; +} + diff --git a/unit-tests/test-cases/main-stripped/Makefile b/unit-tests/test-cases/main-stripped/Makefile index 03756ff..784ba44 100644 --- a/unit-tests/test-cases/main-stripped/Makefile +++ b/unit-tests/test-cases/main-stripped/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,9 +30,11 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} main.c -o main-${ARCH} -exported_symbols_list main.exp - ${FAIL_IF_BAD_MACHO} main-${ARCH} - nm -m main-${ARCH} | grep _magicSymbol | grep "referenced dynamically" | ${PASS_IFF_STDIN} + ${CC} main.c -o main -exported_symbols_list main.exp + nm -m main | grep _magicSymbol | grep "referenced dynamically" | ${FAIL_IF_EMPTY} + nm -m main | grep _hiddenSymbol | grep "referenced dynamically" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main + clean: - rm main-* + rm main diff --git a/unit-tests/test-cases/main-stripped/main.c b/unit-tests/test-cases/main-stripped/main.c index 1e71f1b..1304615 100644 --- a/unit-tests/test-cases/main-stripped/main.c +++ b/unit-tests/test-cases/main-stripped/main.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,6 +27,9 @@ int magicSymbol = 1; asm(".desc _magicSymbol, 0x10"); +// this symbol will be suppressed by .exp file +int hiddenSymbol = 1; +asm(".desc _hiddenSymbol, 0x10"); int main() { diff --git a/unit-tests/test-cases/main-stripped/main.exp b/unit-tests/test-cases/main-stripped/main.exp index 4eb9e89..1c7daa3 100644 --- a/unit-tests/test-cases/main-stripped/main.exp +++ b/unit-tests/test-cases/main-stripped/main.exp @@ -1 +1,2 @@ _main +_magicSymbol diff --git a/unit-tests/test-cases/no-uuid/Makefile b/unit-tests/test-cases/no-uuid/Makefile index 5d52553..7c5304b 100644 --- a/unit-tests/test-cases/no-uuid/Makefile +++ b/unit-tests/test-cases/no-uuid/Makefile @@ -47,8 +47,8 @@ all: ${FAIL_IF_BAD_MACHO} foo ${OTOOL} -hlv foo | grep LC_UUID | ${FAIL_IF_STDIN} -# Test ld -r of stabs file has no uuid - ${CC} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ +# Test ld -r of stabs file has no uuid (llvm does not support stabs, so use gcc) + gcc-4.2 -arch ${ARCH} ${CCFLAGS} foo.c -c -o foo.o -gfull -gstabs+ ${LD} -arch ${ARCH} foo.o -r -o foo2.o ${OTOOL} -hlv foo2.o | grep LC_UUID | ${FAIL_IF_STDIN} diff --git a/unit-tests/test-cases/non-lazy-r/Makefile b/unit-tests/test-cases/non-lazy-r/Makefile index d581048..3d594fd 100644 --- a/unit-tests/test-cases/non-lazy-r/Makefile +++ b/unit-tests/test-cases/non-lazy-r/Makefile @@ -38,6 +38,8 @@ all-i386: hasnl all-armv6: hasnl +all-armv7: hasnl + all-x86_64: all-true all-true: @@ -49,15 +51,18 @@ hasnl: ${CC} ${CCFLAGS} -c other.c -o other.o ${LD} -r -arch ${ARCH} foo.o other.o -o fooall.o -exported_symbol _foo # make sure there are two indirect symbols: _foo and LOCAL - otool -Iv fooall.o | grep "3 entries" | ${FAIL_IF_EMPTY} + otool -Iv fooall.o | grep "4 entries" | ${FAIL_IF_EMPTY} otool -Iv fooall.o | grep _foo | ${FAIL_IF_EMPTY} otool -Iv fooall.o | grep _tent | ${FAIL_IF_EMPTY} otool -Iv fooall.o | grep _other | ${FAIL_IF_STDIN} - # make sure re-parsed correctly - ${OBJECTDUMP} fooall.o | grep name: | grep '_foo$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${OBJECTDUMP} fooall.o | grep name: | grep '_other$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${OBJECTDUMP} fooall.o | grep name: | grep '_tent$$non_lazy_ptr' | ${FAIL_IF_EMPTY} - ${PASS_IFF} true + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to:_foo' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to-local:_other' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to:_tent' | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} fooall.o | grep name: | grep 'non-lazy-pointer-to:_foo' | ${FAIL_IF_EMPTY} + ${LD} -r -arch ${ARCH} fooall.o -o fooall2.o + ${OBJECTDUMP} fooall.o > fooall.dump + ${OBJECTDUMP} fooall2.o > fooall2.dump + ${PASS_IFF} diff fooall.dump fooall2.dump clean: - rm -rf *.o + rm -rf *.o fooall.dump fooall2.dump diff --git a/unit-tests/test-cases/non-lazy-r/foo.c b/unit-tests/test-cases/non-lazy-r/foo.c index 9d21475..bfde3de 100644 --- a/unit-tests/test-cases/non-lazy-r/foo.c +++ b/unit-tests/test-cases/non-lazy-r/foo.c @@ -15,3 +15,6 @@ extern int tent; int gettent() { return tent; } + +extern void* func; +void* getfunc() { return func; } diff --git a/unit-tests/test-cases/non-lazy-r/other.c b/unit-tests/test-cases/non-lazy-r/other.c index 68eb01d..8ee4784 100644 --- a/unit-tests/test-cases/non-lazy-r/other.c +++ b/unit-tests/test-cases/non-lazy-r/other.c @@ -1,3 +1,4 @@ int foo = 2; int other = 3; int tent; +void func() {} diff --git a/unit-tests/test-cases/non-lazy-sections-r/Makefile b/unit-tests/test-cases/non-lazy-sections-r/Makefile new file mode 100644 index 0000000..020cd0a --- /dev/null +++ b/unit-tests/test-cases/non-lazy-sections-r/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that non-lazy-pointers in different section are properly handled by -r +# + + +all: all-${ARCH} + +all-ppc: hasnl + +all-i386: hasnl + +all-armv6: hasnl + +all-armv7: hasnl + +all-x86_64: all-true + +all-true: + ${PASS_IFF} true + + +hasnl: + ${CC} ${CCFLAGS} -c foo.s -o foo.o + ${LD} -r -arch ${ARCH} foo.o -o foo-r.o + ${OBJECTDUMP} foo.o > foo.o.dump + ${OBJECTDUMP} foo-r.o > foo-r.o.dump + ${PASS_IFF} diff foo.o.dump foo-r.o.dump + +clean: + rm -rf *.o *.dump diff --git a/unit-tests/test-cases/non-lazy-sections-r/foo.s b/unit-tests/test-cases/non-lazy-sections-r/foo.s new file mode 100644 index 0000000..d307880 --- /dev/null +++ b/unit-tests/test-cases/non-lazy-sections-r/foo.s @@ -0,0 +1,34 @@ + .text + .globl _test +_test: +#if __i386__ + movl L_foo$non_lazy_ptr, %eax + movl L_bar$non_lazy_ptr, %eax + movl L_other$non_lazy_ptr, %eax + ret +#endif +#if __arm__ || __ppc__ + .long L_foo$non_lazy_ptr + .long L_bar$non_lazy_ptr + .long L_other$non_lazy_ptr +#endif + + + + .section __IMPORT,__pointers,non_lazy_symbol_pointers +L_foo$non_lazy_ptr: +.indirect_symbol _foo + .long 0 + + .section __DATA,__one,non_lazy_symbol_pointers +L_bar$non_lazy_ptr: +.indirect_symbol _bar + .long 0 + + .section __DATA,__two,non_lazy_symbol_pointers +L_other$non_lazy_ptr: +.indirect_symbol _other + .long 0 + + +.subsections_via_symbols diff --git a/unit-tests/test-cases/objc-abi/Makefile b/unit-tests/test-cases/objc-abi/Makefile new file mode 100644 index 0000000..d7eb660 --- /dev/null +++ b/unit-tests/test-cases/objc-abi/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify -objc_abi_version 2 works for i386 +# +ifeq (${ARCH},i386) + ALL = all-i386 +else + ALL = all +endif + +run: ${ALL} + +all: + ${PASS_IFF_GOOD_MACHO} /usr/bin/true + +all-i386: + ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 + size -l test1 | grep __image_info | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} test.m -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2 + size -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} test2 + + +clean: + rm -rf test1 test2 diff --git a/unit-tests/test-cases/objc-abi/test.m b/unit-tests/test-cases/objc-abi/test.m new file mode 100644 index 0000000..8b4a295 --- /dev/null +++ b/unit-tests/test-cases/objc-abi/test.m @@ -0,0 +1,17 @@ + +@interface Foo +@end + +@implementation Foo +@end + + +int main() +{ + return 0; +} + + +#if __i386__ && __OBJC2__ + int _objc_empty_vtable = 1; +#endif diff --git a/unit-tests/test-cases/objc-category-archive/Makefile b/unit-tests/test-cases/objc-category-archive/Makefile new file mode 100644 index 0000000..b56bacf --- /dev/null +++ b/unit-tests/test-cases/objc-category-archive/Makefile @@ -0,0 +1,27 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify -ObjC works with x86_64 categories +# Verify stripped archives keep don't-strip bit +# + +run: all + +all: + ${CC} ${CCFLAGS} -g test.m -c -o test.o + libtool -static test.o -o libtest.a + ${CC} ${CCFLAGS} main.m libtest.a -ObjC -o main -framework Foundation + nm main | grep mycatmethod1 | ${FAIL_IF_EMPTY} + nm main | grep mycatmethod2 | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -r -S -keep_private_externs test.o -o test-stripped.o + libtool -static test-stripped.o -o libtest-stripped.a + ${CC} ${CCFLAGS} main.m libtest-stripped.a -all_load -dead_strip -o main2 -framework Foundation + nm main2 | grep mycatmethod1 | ${FAIL_IF_EMPTY} + nm main2 | grep mycatmethod2 | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main2 + +clean: + rm -rf test.o test-stripped.o libtest.a libtest-stripped.a main main2 diff --git a/unit-tests/test-cases/objc-category-archive/main.m b/unit-tests/test-cases/objc-category-archive/main.m new file mode 100644 index 0000000..1105885 --- /dev/null +++ b/unit-tests/test-cases/objc-category-archive/main.m @@ -0,0 +1,6 @@ + +int main() +{ + return 0; +} + diff --git a/unit-tests/test-cases/objc-category-archive/test.m b/unit-tests/test-cases/objc-category-archive/test.m new file mode 100644 index 0000000..5347856 --- /dev/null +++ b/unit-tests/test-cases/objc-category-archive/test.m @@ -0,0 +1,21 @@ + +#include <Foundation/Foundation.h> + +int some_global_to_stop_libtool_warning = 5; + +@interface NSObject (stuff) +- (void) mycatmethod1; +@end + +@implementation NSObject (stuff) +- (void) mycatmethod1 { } +@end + +@interface NSObject (other) +- (void) mycatmethod2; +@end + +@implementation NSObject (other) +- (void) mycatmethod2 { } +@end + diff --git a/unit-tests/test-cases/objc-category-optimize-load/Makefile b/unit-tests/test-cases/objc-category-optimize-load/Makefile new file mode 100644 index 0000000..5652312 --- /dev/null +++ b/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify optimization where categories are merged into classes +# +OPTIONS = + +ifeq ($(ARCH),i386) + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 +endif + +all: all-${ARCH} + +all-ppc: + ${PASS_IFF} true + +all-i386: all-rest +all-x86_64: all-rest +all-armv6: all-rest + +all-rest: + # check optimzation of category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m -framework Foundation -o libfoo.dylib + size -l libfoo.dylib | grep "__objc_catlist:" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + + +clean: + rm -rf libfoo.dylib diff --git a/unit-tests/test-cases/objc-category-optimize-load/cat1.m b/unit-tests/test-cases/objc-category-optimize-load/cat1.m new file mode 100644 index 0000000..06a78f0 --- /dev/null +++ b/unit-tests/test-cases/objc-category-optimize-load/cat1.m @@ -0,0 +1,15 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +-(void) method1; +@end + + +@interface Foo(mycat) ++(void) load; +@end + +@implementation Foo(mycat) ++(void) load {} +@end + diff --git a/unit-tests/test-cases/objc-category-optimize-load/foo.m b/unit-tests/test-cases/objc-category-optimize-load/foo.m new file mode 100644 index 0000000..065450c --- /dev/null +++ b/unit-tests/test-cases/objc-category-optimize-load/foo.m @@ -0,0 +1,9 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +@end + + +@implementation Foo +@end + diff --git a/unit-tests/test-cases/objc-category-optimize/Makefile b/unit-tests/test-cases/objc-category-optimize/Makefile new file mode 100644 index 0000000..c452bec --- /dev/null +++ b/unit-tests/test-cases/objc-category-optimize/Makefile @@ -0,0 +1,71 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify optimization where categories are merged into classes +# +OPTIONS = + +ifeq ($(ARCH),i386) + OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 +endif + +all: all-${ARCH} + +all-ppc: + ${PASS_IFF} true + +all-i386: all-rest +all-x86_64: all-rest +all-armv6: all-rest + +all-rest: + # check optimization can be turned off + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -Wl,-no_objc_category_merging -o libno.dylib + size -l libno.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_STDIN} + otool -ov libno.dylib | grep -A17 __objc_classlist | grep -A16 '_OBJC_CLASS_$$_Foo' | grep "count 1" | ${FAIL_IF_EMPTY} + # check optimzation of category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -framework Foundation -o libfoo.dylib + size -l libfoo.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 4" | ${FAIL_IF_EMPTY} + # check optimzation of protocol and category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROTOCOLS -framework Foundation -o libfoo2.dylib + size -l libfoo2.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo2.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} + # check optimzation of properties and category methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DPROPERTIES -framework Foundation -o libfoo3.dylib + size -l libfoo3.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo3.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 6" | ${FAIL_IF_EMPTY} + # check optimzation of category methods and no base methods + ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat1.m cat2.m -DNO_BASE_METHODS -framework Foundation -o libfoo4.dylib + size -l libfoo4.dylib | grep "__objc_catlist: 0" | ${FAIL_IF_EMPTY} + otool -ov libfoo4.dylib | grep -A20 __objc_classlist | grep -A20 '_OBJC_CLASS_$$_Foo' | grep "count 3" | ${FAIL_IF_EMPTY} + otool -ov libfoo4.dylib | grep -A20 "Meta Class" | grep "count 2" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo3.dylib + + + +clean: + rm -rf lib*.dylib diff --git a/unit-tests/test-cases/objc-category-optimize/cat1.m b/unit-tests/test-cases/objc-category-optimize/cat1.m new file mode 100644 index 0000000..11c170b --- /dev/null +++ b/unit-tests/test-cases/objc-category-optimize/cat1.m @@ -0,0 +1,29 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +-(void) method1; +@end + + + + +@interface Foo(mycat) +-(void) instance_method_mycat1; +-(void) instance_method_mycat2; ++(void) class_method_mycat; +#if PROPERTIES + @property(readonly) int property1; + @property(readonly) int property2; +#endif +@end + +@implementation Foo(mycat) +-(void) instance_method_mycat1 {} +-(void) instance_method_mycat2 {} ++(void) class_method_mycat {} +#if PROPERTIES + -(int) property1 { return 0; } + -(int) property2 { return 0; } +#endif +@end + diff --git a/unit-tests/test-cases/objc-category-optimize/cat2.m b/unit-tests/test-cases/objc-category-optimize/cat2.m new file mode 100644 index 0000000..7b51291 --- /dev/null +++ b/unit-tests/test-cases/objc-category-optimize/cat2.m @@ -0,0 +1,37 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +-(void) method1; +@end + +#if PROTOCOLS + @protocol myotherprotocol + - (void) instance_method_myotherprotocol1; + - (void) instance_method_myotherprotocol2; + @end + + @interface Foo(myothercat) < myotherprotocol > + - (void) instance_method_myothercat; + + (void) class_method_myothercat; + @end + + @implementation Foo(myothercat) + - (void) instance_method_myothercat {} + + (void) class_method_myothercat {} + - (void) instance_method_myotherprotocol1 {} + - (void) instance_method_myotherprotocol2 {} + @end + +#else + @interface Foo(myothercat) + - (void) instance_method_myothercat; + + (void) class_method_myothercat; + @end + + @implementation Foo(myothercat) + - (void) instance_method_myothercat {} + + (void) class_method_myothercat {} + @end +#endif + + diff --git a/unit-tests/test-cases/objc-category-optimize/foo.m b/unit-tests/test-cases/objc-category-optimize/foo.m new file mode 100644 index 0000000..0509927 --- /dev/null +++ b/unit-tests/test-cases/objc-category-optimize/foo.m @@ -0,0 +1,17 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +#ifndef NO_BASE_METHODS +-(void) instance_method; ++(void) class_method; +#endif +@end + + +@implementation Foo +#ifndef NO_BASE_METHODS +-(void) instance_method {} ++(void) class_method {} +#endif +@end + diff --git a/unit-tests/test-cases/objc-class-alias/Makefile b/unit-tests/test-cases/objc-class-alias/Makefile new file mode 100644 index 0000000..c8c1b27 --- /dev/null +++ b/unit-tests/test-cases/objc-class-alias/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify -alias works with ObjC classes +# +CLASS_NAME_FOO = .objc_class_name_Foo +ifeq (${ARCH},x86_64) + CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo' +endif + + +run: all + +all: + ${CC} ${CCFLAGS} test.m -Wl,-alias,${CLASS_NAME_FOO},_MagicName -framework Foundation -o test + ${DYLDINFO} -export test | grep ${CLASS_NAME_FOO} | awk '{ print $$1}' > foo.addr + ${DYLDINFO} -export test | grep _MagicName | awk '{ print $$1}' > magic.addr + ${PASS_IFF} diff foo.addr magic.addr + +clean: + rm -rf test foo.addr magic.addr diff --git a/unit-tests/test-cases/objc-class-alias/test.m b/unit-tests/test-cases/objc-class-alias/test.m new file mode 100644 index 0000000..7eba08f --- /dev/null +++ b/unit-tests/test-cases/objc-class-alias/test.m @@ -0,0 +1,13 @@ +#include <Foundation/Foundation.h> + +@interface Foo : NSObject +@end + +@implementation Foo +@end + + +int main() +{ + return 0; +} diff --git a/unit-tests/test-cases/objc-gc-checks/Makefile b/unit-tests/test-cases/objc-gc-checks/Makefile index 0abe7d5..b18012a 100644 --- a/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/unit-tests/test-cases/objc-gc-checks/Makefile @@ -26,6 +26,16 @@ include ${TESTROOT}/include/common.makefile SHELL = bash # use bash shell so we can redirect just stderr +IMAGE_INFO = "__image_info" + +ifeq ($(ARCH),x86_64) + IMAGE_INFO = "__objc_imageinfo" +endif +ifeq ($(ARCH),armv6) + IMAGE_INFO = "__objc_imageinfo" +endif + + # # Validate that the linker catches illegal combinations of .o files @@ -70,6 +80,11 @@ test: ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + # check GC/RR + RR -> RR + ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib + ${FAIL_IF_BAD_MACHO} libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} + # check GC + GC/RR -> GC ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib ${FAIL_IF_BAD_MACHO} libfoobar.dylib @@ -78,7 +93,42 @@ test: # check RR + GC -> error ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log + # check cmd line GC/RR, GC/RR + RR -> error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc 2> fail.log + + # check GC/RR + compaction + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x12 | ${FAIL_IF_EMPTY} + + # check GC + compaction + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x16 | ${FAIL_IF_EMPTY} + + # none + GC/RR-dylib -> none + ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + + # none + GC-dylib -> none + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + + # none + RR-dylib -> none + ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} + + # check RR + GC-dylib -> error + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log + + # check GC + RR-dylib -> error + ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log + + ${PASS_IFF} true clean: - rm -rf foo*.o bar*.o libfoobar.dylib fail.log + rm -rf foo*.o bar*.o libfoobar.dylib fail.log libfoo.dylib libnone.dylib diff --git a/unit-tests/test-cases/objc-gc-checks/none.c b/unit-tests/test-cases/objc-gc-checks/none.c new file mode 100644 index 0000000..44506fa --- /dev/null +++ b/unit-tests/test-cases/objc-gc-checks/none.c @@ -0,0 +1 @@ +void none() {} diff --git a/unit-tests/test-cases/objc-literal-pointers/Makefile b/unit-tests/test-cases/objc-literal-pointers/Makefile index c0b9081..6596f83 100644 --- a/unit-tests/test-cases/objc-literal-pointers/Makefile +++ b/unit-tests/test-cases/objc-literal-pointers/Makefile @@ -36,10 +36,10 @@ run: all all: ${CC} ${CCFLAGS} test.m -c -o test.o - ${OBJECTDUMP} -no_content test.o | grep -v zero-fill-at> test.dump + ${OBJECTDUMP} -no_content test.o > test.dump ${LD} -arch ${ARCH} -r test.o -keep_private_externs -o test-r.o - ${OBJECTDUMP} -no_content test-r.o | grep -v zero-fill-at > test-r.dump + ${OBJECTDUMP} -no_content test-r.o > test-r.dump diff test.dump test-r.dump | ${PASS_IFF_EMPTY} diff --git a/unit-tests/test-cases/objc-properties/Makefile b/unit-tests/test-cases/objc-properties/Makefile new file mode 100644 index 0000000..999bf2f --- /dev/null +++ b/unit-tests/test-cases/objc-properties/Makefile @@ -0,0 +1,19 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Verify an Objective-C object file when run through ld -r is unaltered. +# +# + +run: all + +all: + ${CC} ${CCFLAGS} test.m -dynamiclib -framework Foundation -o libtest.dylib + ${CC} ${CCFLAGS} test.m -dynamiclib -framework Foundation -o libtest.dylib -Wl,-no_compact_linkedit -ldylib1.o + ${PASS_IFF_GOOD_MACHO} libtest.dylib + +clean: + rm -rf libtest.dylib diff --git a/unit-tests/test-cases/objc-properties/test.m b/unit-tests/test-cases/objc-properties/test.m new file mode 100644 index 0000000..25ca8c8 --- /dev/null +++ b/unit-tests/test-cases/objc-properties/test.m @@ -0,0 +1,49 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <Foundation/Foundation.h> + +@interface Test +{ + BOOL one; + NSString* two; + BOOL three; +} +@property BOOL one; +@property (retain) NSString* two; +@property BOOL three; +@end + + +@implementation Test +@synthesize one; +@synthesize two; +@synthesize three; +@end + + +int main() +{ + return 0; +} diff --git a/unit-tests/test-cases/objc-visibility/Makefile b/unit-tests/test-cases/objc-visibility/Makefile new file mode 100644 index 0000000..bd0eb15 --- /dev/null +++ b/unit-tests/test-cases/objc-visibility/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2010 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Tests when a hidden class is made file scoped (via ld -r) +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.m -c -o foo.o + ${CC} ${CCFLAGS} bar.m -c -o bar.o + ${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libtfoobar.dylib -framework Foundation + ${LD} -r -arch ${ARCH} foo.o bar.o -o bar2.o + ${CC} ${CCFLAGS} bar2.o -dynamiclib -o liball.dylib -framework Foundation + ${PASS_IFF_GOOD_MACHO} liball.dylib + +clean: + rm -rf *.o *.dylib diff --git a/unit-tests/test-cases/objc-visibility/bar.h b/unit-tests/test-cases/objc-visibility/bar.h new file mode 100644 index 0000000..dc37fe1 --- /dev/null +++ b/unit-tests/test-cases/objc-visibility/bar.h @@ -0,0 +1,8 @@ + +#include <Foundation/Foundation.h> + +__attribute__((visibility("hidden"))) +@interface Bar : NSData +- (NSArray*) bar; +@end + diff --git a/unit-tests/test-cases/objc-visibility/bar.m b/unit-tests/test-cases/objc-visibility/bar.m new file mode 100644 index 0000000..997ac26 --- /dev/null +++ b/unit-tests/test-cases/objc-visibility/bar.m @@ -0,0 +1,13 @@ +#include <Foundation/Foundation.h> + + +#include "bar.h" + + +@implementation Bar +- (NSArray*) bar +{ + return [NSArray array]; +} +@end + diff --git a/unit-tests/test-cases/objc-visibility/foo.h b/unit-tests/test-cases/objc-visibility/foo.h new file mode 100644 index 0000000..e22d744 --- /dev/null +++ b/unit-tests/test-cases/objc-visibility/foo.h @@ -0,0 +1,8 @@ + +#include <Foundation/Foundation.h> + + +@interface Foo : NSObject +- (NSString*) foo; +@end + diff --git a/unit-tests/test-cases/objc-visibility/foo.m b/unit-tests/test-cases/objc-visibility/foo.m new file mode 100644 index 0000000..4882e7b --- /dev/null +++ b/unit-tests/test-cases/objc-visibility/foo.m @@ -0,0 +1,14 @@ + +#include <Foundation/Foundation.h> + +#include "foo.h" +#include "bar.h" + +@implementation Foo +- (NSString*) foo +{ + [Bar alloc]; + return [NSString stringWithUTF8String:"hello"]; +} +@end + diff --git a/unit-tests/test-cases/order_file-ans/main.cxx b/unit-tests/test-cases/order_file-ans/main.cxx index b3c1edd..4ae8026 100644 --- a/unit-tests/test-cases/order_file-ans/main.cxx +++ b/unit-tests/test-cases/order_file-ans/main.cxx @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * Copyright (c) 2007-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -55,5 +55,9 @@ namespace wow { int main() { + wow::inner(); + baz(NULL); + bar(NULL); + foo(); return 0; } diff --git a/unit-tests/test-cases/order_file/Makefile b/unit-tests/test-cases/order_file/Makefile index 21ae0ea..1aec6f1 100644 --- a/unit-tests/test-cases/order_file/Makefile +++ b/unit-tests/test-cases/order_file/Makefile @@ -33,25 +33,29 @@ include ${TESTROOT}/include/common.makefile run: all all: - as -arch ${ARCH} -L extra.s -o extra.o - ${CC} ${CCFLAGS} main.c extra.o -o main1 -Wl,-order_file -Wl,main1.order + ${CC} ${CCFLAGS} main.c extra.s -o main1 -Wl,-order_file -Wl,main1.order ${FAIL_IF_BAD_MACHO} main1 nm -n -g -j main1 | grep "_main" > main1.nm ${PASS_IFF} diff main1.nm main1.expected - ${CC} ${CCFLAGS} main.c extra.o -o main2 -Wl,-order_file -Wl,main2.order + ${CC} ${CCFLAGS} main.c extra.s -o main2 -Wl,-order_file -Wl,main2.order ${FAIL_IF_BAD_MACHO} main2 nm -n -j main2 | egrep '^_[a-z]+[0-9]$$' > main2.nm ${PASS_IFF} diff main2.nm main2.expected - ${CC} -arch ${ARCH} -c main.c -o main.o - ${CC} ${CCFLAGS} main.o extra.o -o main3 -Wl,-order_file -Wl,main3.order + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o extra.s -o main3 -Wl,-order_file -Wl,main3.order ${FAIL_IF_BAD_MACHO} main3 nm -n -g -j main3 | grep "_main" > main3.nm ${PASS_IFF} diff main3.nm main3.expected + ${CC} ${CCFLAGS} main.c extra.s -DSUBSECTIONS=1 -o main4 -Wl,-order_file -Wl,main4.order + ${FAIL_IF_BAD_MACHO} main4 + nm -n -g -j main4 | nm -njg main4 | egrep '*[1-9]' > main4.nm + ${PASS_IFF} diff main4.nm main4.expected + clean: - rm -rf main1 *.nm main2 *.o warnings.log main3 + rm -rf main1 main2 main3 main4 main.o *.nm diff --git a/unit-tests/test-cases/order_file/extra.s b/unit-tests/test-cases/order_file/extra.s index 90166ce..eba52c0 100644 --- a/unit-tests/test-cases/order_file/extra.s +++ b/unit-tests/test-cases/order_file/extra.s @@ -7,6 +7,8 @@ _foo1: nop .globl _aaa2 _aaa2: + .globl _bbb2 + .private_extern _bbb2 _bbb2: _ccc2: nop @@ -20,5 +22,7 @@ _ccc3: _aaa4: nop - - \ No newline at end of file + +#if SUBSECTIONS + .subsections_via_symbols +#endif diff --git a/unit-tests/test-cases/order_file/main4.expected b/unit-tests/test-cases/order_file/main4.expected new file mode 100644 index 0000000..b82a23d --- /dev/null +++ b/unit-tests/test-cases/order_file/main4.expected @@ -0,0 +1,6 @@ +_aaa2 +_main3 +_main2 +_main4 +_foo1 +_bbb3 diff --git a/unit-tests/test-cases/order_file/main4.order b/unit-tests/test-cases/order_file/main4.order new file mode 100644 index 0000000..fe07188 --- /dev/null +++ b/unit-tests/test-cases/order_file/main4.order @@ -0,0 +1,4 @@ + + +_bbb2 +_main3 diff --git a/unit-tests/test-cases/re-export-and-use/Makefile b/unit-tests/test-cases/re-export-and-use/Makefile new file mode 100644 index 0000000..b220580 --- /dev/null +++ b/unit-tests/test-cases/re-export-and-use/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a public re-exported library is automatically added as a dependent +# unless nothing is used from it. +# + + +run: all + +all: + + ${CC} ${CCFLAGS} -dynamiclib pub.c -o libpub.dylib -install_name /usr/lib/libpub.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + # verify uses of symbols from re-exported dylibs have ordinal that allows dyld to search amoung re-exported dylibs + ${CC} ${CCFLAGS} -dynamiclib foo.c -DUSE_BAZ -o libfoo2.dylib -Wl,-reexport_library libpub.dylib -Wl,-reexport_library libbar.dylib -Wl,-reexport_library libbaz.dylib + dyldinfo -bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY} + dyldinfo -lazy_bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY} + dyldinfo -bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY} + dyldinfo -lazy_bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY} + # verify that if only one non-publc re-exported dylib, don't use magic ordinal + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo1.dylib -Wl,-reexport_library libpub.dylib -Wl,-reexport_library libbar.dylib + dyldinfo -bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN} + dyldinfo -lazy_bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo2.dylib + + +clean: + + rm -rf libbar.dylib libbaz.dylib libpub.dylib libfoo1.dylib libfoo2.dylib diff --git a/unit-tests/test-cases/re-export-and-use/bar.c b/unit-tests/test-cases/re-export-and-use/bar.c new file mode 100644 index 0000000..642a280 --- /dev/null +++ b/unit-tests/test-cases/re-export-and-use/bar.c @@ -0,0 +1,5 @@ + +int bar() +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-and-use/baz.c b/unit-tests/test-cases/re-export-and-use/baz.c new file mode 100644 index 0000000..98b7832 --- /dev/null +++ b/unit-tests/test-cases/re-export-and-use/baz.c @@ -0,0 +1,5 @@ + +int baz() +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-and-use/foo.c b/unit-tests/test-cases/re-export-and-use/foo.c new file mode 100644 index 0000000..ad634d3 --- /dev/null +++ b/unit-tests/test-cases/re-export-and-use/foo.c @@ -0,0 +1,16 @@ +extern int bar(); +extern int baz(); + +void* pbar = &bar; + +#if USE_BAZ +void* pbaz = &baz; +#endif + +int foo() +{ +#if USE_BAZ + baz(); +#endif + return bar(); +} diff --git a/unit-tests/test-cases/re-export-and-use/pub.c b/unit-tests/test-cases/re-export-and-use/pub.c new file mode 100644 index 0000000..03ffc1b --- /dev/null +++ b/unit-tests/test-cases/re-export-and-use/pub.c @@ -0,0 +1,5 @@ + +int pub() +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-layers/Makefile b/unit-tests/test-cases/re-export-layers/Makefile new file mode 100644 index 0000000..fc62273 --- /dev/null +++ b/unit-tests/test-cases/re-export-layers/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test all the different ways that re-exports can be specified and implemented +# + + +run: all + +all: + + +# -reexport_library for 10.4 + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.4 + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${FAIL_IF_BAD_MACHO} main + + +# -reexport_library for 10.5 and later + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${FAIL_IF_BAD_MACHO} libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${CC} ${CCFLAGS} main.c libfoo.dylib -o main + ${PASS_IFF_GOOD_MACHO} main + + +clean: + + rm -rf libbaz.dylib libbar.dylib libfoo.dylib main + diff --git a/unit-tests/test-cases/re-export-layers/bar.c b/unit-tests/test-cases/re-export-layers/bar.c new file mode 100644 index 0000000..9c18401 --- /dev/null +++ b/unit-tests/test-cases/re-export-layers/bar.c @@ -0,0 +1,5 @@ + +int bar (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-layers/baz.c b/unit-tests/test-cases/re-export-layers/baz.c new file mode 100644 index 0000000..af6a9f8 --- /dev/null +++ b/unit-tests/test-cases/re-export-layers/baz.c @@ -0,0 +1,5 @@ + +int baz (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-layers/foo.c b/unit-tests/test-cases/re-export-layers/foo.c new file mode 100644 index 0000000..d0cdf47 --- /dev/null +++ b/unit-tests/test-cases/re-export-layers/foo.c @@ -0,0 +1,4 @@ +int foo (void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-layers/main.c b/unit-tests/test-cases/re-export-layers/main.c new file mode 100644 index 0000000..5f1fae9 --- /dev/null +++ b/unit-tests/test-cases/re-export-layers/main.c @@ -0,0 +1,15 @@ + + +extern void baz(); +extern void bar(); +extern void foo(); + + +int main() +{ + baz(); + bar(); + foo(); + return 0; +} + diff --git a/unit-tests/test-cases/re-export-symbol/Makefile b/unit-tests/test-cases/re-export-symbol/Makefile new file mode 100644 index 0000000..6de347c --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that fine grain re-exports works +# + +run: all + +all: + # build base library + ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + + # build library the re-exports _bar from base library + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -exported_symbols_list foo.exp + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${DYLDINFO} -export libfoo.dylib | grep _bar | grep 're-export' | ${FAIL_IF_EMPTY} + # link against dylib and verify _bar is marked as coming from libfoo + ${CC} ${CCFLAGS} main1.c libfoo.dylib -o main1 + ${DYLDINFO} -bind -lazy_bind main1 | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} + + # build library the re-exports _bar from base library as _mybar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp + ${FAIL_IF_BAD_MACHO} libfoo2.dylib + ${DYLDINFO} -export libfoo2.dylib | grep _mybar | grep 're-export' | grep _bar | ${FAIL_IF_EMPTY} + # link against dylib and verify _mybar is marked as coming from libfoo + ${CC} ${CCFLAGS} main2.c libfoo2.dylib -o main2 + ${DYLDINFO} -bind -lazy_bind main2 | grep _mybar | grep libfoo2 | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} libfoo2.dylib + +clean: + rm -rf libbar.dylib libfoo.dylib libfoo2.dylib main1 main2 diff --git a/unit-tests/test-cases/re-export-symbol/bar.c b/unit-tests/test-cases/re-export-symbol/bar.c new file mode 100644 index 0000000..34e5666 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-symbol/foo.c b/unit-tests/test-cases/re-export-symbol/foo.c new file mode 100644 index 0000000..714540a --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/unit-tests/test-cases/re-export-symbol/foo.exp b/unit-tests/test-cases/re-export-symbol/foo.exp new file mode 100644 index 0000000..b9e50b8 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/foo.exp @@ -0,0 +1,2 @@ +_foo +_bar diff --git a/unit-tests/test-cases/re-export-symbol/foo2.exp b/unit-tests/test-cases/re-export-symbol/foo2.exp new file mode 100644 index 0000000..d55b748 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/foo2.exp @@ -0,0 +1,2 @@ +_foo +_mybar diff --git a/unit-tests/test-cases/re-export-symbol/main1.c b/unit-tests/test-cases/re-export-symbol/main1.c new file mode 100644 index 0000000..4c0b25e --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/main1.c @@ -0,0 +1,9 @@ +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/unit-tests/test-cases/re-export-symbol/main2.c b/unit-tests/test-cases/re-export-symbol/main2.c new file mode 100644 index 0000000..edac057 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/main2.c @@ -0,0 +1,10 @@ +extern int foo(); +extern int mybar(); + +int main() +{ + foo(); + mybar(); + return 0; +} + diff --git a/unit-tests/test-cases/read-only-relocs/Makefile b/unit-tests/test-cases/read-only-relocs/Makefile index 02ed1df..8db5b03 100644 --- a/unit-tests/test-cases/read-only-relocs/Makefile +++ b/unit-tests/test-cases/read-only-relocs/Makefile @@ -32,15 +32,24 @@ SHELL = bash # use bash shell so we can redirect just stderr NO_PIC = STATIC = +RELOC_FAIL = ${FAIL_IF_SUCCESS} +LRELOCS_NEEDED = ${FAIL_IF_EMPTY} +XRELOCS_NEEDED = ${FAIL_IF_EMPTY} ifeq (${ARCH},i386) NO_PIC = -mdynamic-no-pic STATIC = -static -else - ifeq (${ARCH},ppc) - NO_PIC = -mdynamic-no-pic - STATIC = -mdynamic-no-pic - endif +endif +ifeq (${ARCH},ppc) + NO_PIC = -mdynamic-no-pic + STATIC = -mdynamic-no-pic + XRELOCS_NEEDED = ${FAIL_IF_STDIN} + LRELOCS_NEEDED = ${FAIL_IF_EMPTY} +endif +ifeq (${ARCH},x86_64) + RELOC_FAIL = + XRELOCS_NEEDED = ${FAIL_IF_STDIN} + LRELOCS_NEEDED = ${FAIL_IF_STDIN} endif @@ -48,16 +57,25 @@ endif all: # build libfoo.dylib as regular dylib ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + # build libtest.dylib using -mdynamic-no-pic, should fail + ${CC} ${CCFLAGS} test_rebase.c -c ${NO_PIC} + ${RELOC_FAIL} ${CC} ${CCFLAGS} test_rebase.o libfoo.dylib -dynamiclib -o libtestrebase.dylib -read_only_relocs error 2>/dev/null + ${CC} ${CCFLAGS} test_bind.c -c ${NO_PIC} + ${RELOC_FAIL} ${CC} ${CCFLAGS} test_bind.o libfoo.dylib -dynamiclib -o libtestbind.dylib -read_only_relocs error 2>/dev/null # build libtest.dylib using -mdynamic-no-pic and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${NO_PIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + ${CC} ${CCFLAGS} test_rebase.c -c ${NO_PIC} + ${CC} ${CCFLAGS} test_bind.c -c ${NO_PIC} + ${CC} ${CCFLAGS} test_rebase.o test_bind.o libfoo.dylib -dynamiclib -o libtest-no-pic.dylib -read_only_relocs suppress -Wl,-w # build libtest.dylib using -static and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${STATIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -dynamiclib -o libtest.dylib -read_only_relocs suppress -Wl,-w + ${CC} ${CCFLAGS} test_rebase.c -c ${STATIC} + ${CC} ${CCFLAGS} test_bind.c -c ${STATIC} + ${CC} ${CCFLAGS} test_rebase.o test_bind.o libfoo.dylib -dynamiclib -o libtest-static.dylib -read_only_relocs suppress -Wl,-w + otool -lv libtest-static.dylib | grep -A9 "sectname __text" | grep attributes | grep EXT_RELOC | ${XRELOCS_NEEDED} + otool -lv libtest-static.dylib | grep -A9 "sectname __text" | grep attributes | grep LOC_RELOC | ${LRELOCS_NEEDED} # build main using -static and -read_only_relocs suppress - ${CC} ${CCFLAGS} test.c -c ${STATIC} - ${CC} ${CCFLAGS} test.o libfoo.dylib -o foo -read_only_relocs suppress -Wl,-w - ${PASS_IFF_GOOD_MACHO} foo + ${CC} ${CCFLAGS} main.c -c ${STATIC} + ${CC} ${CCFLAGS} main.o libfoo.dylib -o main -read_only_relocs suppress -Wl,-w + ${PASS_IFF_GOOD_MACHO} main clean: - rm -rf test.o libfoo.dylib libtest.dylib foo + rm -rf test_bind.o test_rebase.o libfoo.dylib libtestrebase.dylib libtestbind.dylib libtest-no-pic.dylib libtest-static.dylib main.o main diff --git a/unit-tests/test-cases/read-only-relocs/main.c b/unit-tests/test-cases/read-only-relocs/main.c new file mode 100644 index 0000000..d5fe9c2 --- /dev/null +++ b/unit-tests/test-cases/read-only-relocs/main.c @@ -0,0 +1,10 @@ + +extern int b; +extern void func(); + +int main() +{ + func(); + return b; +} + diff --git a/unit-tests/test-cases/read-only-relocs/test_bind.c b/unit-tests/test-cases/read-only-relocs/test_bind.c new file mode 100644 index 0000000..8ec6b8d --- /dev/null +++ b/unit-tests/test-cases/read-only-relocs/test_bind.c @@ -0,0 +1,10 @@ + +extern int b; +extern void func(); + +int test_bind() +{ + func(); + return b; +} + diff --git a/unit-tests/test-cases/read-only-relocs/test_rebase.c b/unit-tests/test-cases/read-only-relocs/test_rebase.c new file mode 100644 index 0000000..1ddf43c --- /dev/null +++ b/unit-tests/test-cases/read-only-relocs/test_rebase.c @@ -0,0 +1,8 @@ + +int a=0; + +int test_rebase() +{ + return a; +} + diff --git a/unit-tests/test-cases/rebase-basic/Makefile b/unit-tests/test-cases/rebase-basic/Makefile index 9373aa3..cc7407b 100644 --- a/unit-tests/test-cases/rebase-basic/Makefile +++ b/unit-tests/test-cases/rebase-basic/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006-2008 Apple Inc. All rights reserved. +# Copyright (c) 2006-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -32,10 +32,10 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} -c foo.c -o foo.${ARCH}.o + ${CC} ${CCFLAGS} -c -g foo.c -o foo.${ARCH}.o ${FAIL_IF_BAD_OBJ} foo.${ARCH}.o - ${CC} ${CCFLAGS} -c bar.m -o bar.${ARCH}.o + ${CC} ${CCFLAGS} -c -g bar.m -o bar.${ARCH}.o ${FAIL_IF_BAD_OBJ} bar.${ARCH}.o ${CC} ${CCFLAGS} foo.${ARCH}.o bar.${ARCH}.o -dynamiclib -Wl,-no_uuid -Wl,-no_order_data -o libfoo.${ARCH}.dylib -framework Foundation -framework CoreFoundation diff --git a/unit-tests/test-cases/reexport_symbols_list/Makefile b/unit-tests/test-cases/reexport_symbols_list/Makefile new file mode 100644 index 0000000..abc560c --- /dev/null +++ b/unit-tests/test-cases/reexport_symbols_list/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Test -reexport_symbols_list +# + +run: all + +all: + # build base library + ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib + ${FAIL_IF_BAD_MACHO} libbar.dylib + + # verify re-export fails with -bundle + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -bundle foo.c -o libfoo.bundle libbar.dylib -Wl,-reexported_symbols_list,bart.exp 2>/dev/null + + # verify failure if re-exported symbol does not exist + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -Wl,-reexported_symbols_list,junk.exp 2>/dev/null + + # verify failure if re-exported symbol is from .o file + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -Wl,-reexported_symbols_list,foo.exp 2>/dev/null + + # build library the re-exports _bart from base library + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -Wl,-reexported_symbols_list,bart.exp + ${FAIL_IF_BAD_MACHO} libfoo.dylib + ${DYLDINFO} -export libfoo.dylib | grep _barb | ${FAIL_IF_STDIN} + ${DYLDINFO} -export libfoo.dylib | grep _bart | grep 're-export' | ${FAIL_IF_EMPTY} + + # link against dylib and verify _bart is marked as coming from libfoo + ${CC} ${CCFLAGS} main1.c libfoo.dylib -o main1 + ${DYLDINFO} -bind -lazy_bind main1 | grep _bart | grep libfoo | ${FAIL_IF_EMPTY} + + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm -rf libbar.dylib libfoo.dylib main1 diff --git a/unit-tests/test-cases/reexport_symbols_list/bar.c b/unit-tests/test-cases/reexport_symbols_list/bar.c new file mode 100644 index 0000000..b0f6fc9 --- /dev/null +++ b/unit-tests/test-cases/reexport_symbols_list/bar.c @@ -0,0 +1,10 @@ + +int barb(void) +{ + return 1; +} + +int bart(void) +{ + return 0; +} diff --git a/unit-tests/test-cases/reexport_symbols_list/bart.exp b/unit-tests/test-cases/reexport_symbols_list/bart.exp new file mode 100644 index 0000000..da8ec30 --- /dev/null +++ b/unit-tests/test-cases/reexport_symbols_list/bart.exp @@ -0,0 +1 @@ +_bart diff --git a/unit-tests/test-cases/reexport_symbols_list/foo.c b/unit-tests/test-cases/reexport_symbols_list/foo.c new file mode 100644 index 0000000..714540a --- /dev/null +++ b/unit-tests/test-cases/reexport_symbols_list/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/unit-tests/test-cases/reexport_symbols_list/foo.exp b/unit-tests/test-cases/reexport_symbols_list/foo.exp new file mode 100644 index 0000000..70eefe9 --- /dev/null +++ b/unit-tests/test-cases/reexport_symbols_list/foo.exp @@ -0,0 +1 @@ +_foo diff --git a/unit-tests/test-cases/reexport_symbols_list/junk.exp b/unit-tests/test-cases/reexport_symbols_list/junk.exp new file mode 100644 index 0000000..8b925f7 --- /dev/null +++ b/unit-tests/test-cases/reexport_symbols_list/junk.exp @@ -0,0 +1 @@ +_junk diff --git a/unit-tests/test-cases/reexport_symbols_list/main1.c b/unit-tests/test-cases/reexport_symbols_list/main1.c new file mode 100644 index 0000000..4655cb6 --- /dev/null +++ b/unit-tests/test-cases/reexport_symbols_list/main1.c @@ -0,0 +1,9 @@ +extern int foo(); +extern int bart(); + +int main() +{ + foo(); + bart(); + return 0; +} diff --git a/unit-tests/test-cases/relocs-asm/relocs-asm.s b/unit-tests/test-cases/relocs-asm/relocs-asm.s index 4d38f2d..86716da 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -82,6 +82,9 @@ _test_branches: @ call internal + addend bne _test_calls+16 + @ call internal - addend + bne _test_calls-16 + @ call external bne _external @@ -92,6 +95,35 @@ _test_branches: bl 1f 1: nop + + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + + .globl _test_hidden_weak + .private_extern _test_hidden_weak + .weak_definition _test_hidden_weak +_test_hidden_weak: + nop + nop + + +_test_weak_call: + bl _test_weak + bl _test_weak+4 + + +_test_weak_hidden_pointer_call: + ldr r12,L3 + add r12, pc, r12 + nop + bx r12 +L101: + .long _test_hidden_weak - L101 + + .text _pointer_diffs: .long _foo-1b @@ -101,6 +133,7 @@ _pointer_diffs: .long (_test_branches - _test_loads) + -2097152 .long (_test_calls - _test_loads) + -2097152 + .text .code 32 _arm1: @@ -178,8 +211,8 @@ L15:add r3, pc .align 2 L16: .long _thumb1-(L12+8) L17: .long _thumb2-(L13+8) -L17a: .long _thumb3-(L3+8) -L17b: .long _thumb4-(L3+8) +L17a: .long _thumb3-(L13+8) +L17b: .long _thumb4-(L13+8) L18: .long _arm1-(L14+8) L19: .long _arm2-(L15+8) L19a: .long _arm3-(L15+8) @@ -192,6 +225,81 @@ _myVTable: .long _thumb3 .long _arm1 .long _arm2 + +#if __ARM_ARCH_7A__ + .text + .align 2 +_arm16tests: + movw r0, :lower16:_datahilo16 + movt r0, :upper16:_datahilo16 + movw r0, :lower16:_datahilo16+4 + movt r0, :upper16:_datahilo16+4 + movw r0, :lower16:_datahilo16alt + movt r0, :upper16:_datahilo16alt + movw r0, :lower16:_datahilo16alt+61440 + movt r0, :upper16:_datahilo16alt+61440 + movw r0, :lower16:_datahilo16alt+2048 + movt r0, :upper16:_datahilo16alt+2048 + movw r0, :lower16:_datahilo16alt+1792 + movt r0, :upper16:_datahilo16alt+1792 + movw r0, :lower16:_datahilo16alt+165 + movt r0, :upper16:_datahilo16alt+165 +Lpicbase: + movw r0, :lower16:_datahilo16 - Lpicbase + movt r0, :upper16:_datahilo16 - Lpicbase + movw r0, :lower16:_datahilo16+4 - Lpicbase + movt r0, :upper16:_datahilo16+4 - Lpicbase + movw r0, :lower16:_datahilo16alt - Lpicbase + movt r0, :upper16:_datahilo16alt - Lpicbase + movw r0, :lower16:_datahilo16alt+61440 - Lpicbase + movt r0, :upper16:_datahilo16alt+61440 - Lpicbase + movw r0, :lower16:_datahilo16alt+2048 - Lpicbase + movt r0, :upper16:_datahilo16alt+2048 - Lpicbase + movw r0, :lower16:_datahilo16alt+1792 - Lpicbase + movt r0, :upper16:_datahilo16alt+1792 - Lpicbase + movw r0, :lower16:_datahilo16alt+165 - Lpicbase + movt r0, :upper16:_datahilo16alt+165 - Lpicbase + bx lr + + .code 16 + .thumb_func _thumb16tests +_thumb16tests: + movw r0, :lower16:_datahilo16 + movt r0, :upper16:_datahilo16 + movw r0, :lower16:_datahilo16+4 + movt r0, :upper16:_datahilo16+4 + movw r0, :lower16:_datahilo16alt + movt r0, :upper16:_datahilo16alt + movw r0, :lower16:_datahilo16alt+61440 + movt r0, :upper16:_datahilo16alt+61440 + movw r0, :lower16:_datahilo16alt+2048 + movt r0, :upper16:_datahilo16alt+2048 + movw r0, :lower16:_datahilo16alt+1792 + movt r0, :upper16:_datahilo16alt+1792 + movw r0, :lower16:_datahilo16alt+165 + movt r0, :upper16:_datahilo16alt+165 +Lpicbase2: + movw r0, :lower16:_datahilo16 - Lpicbase2 + movt r0, :upper16:_datahilo16 - Lpicbase2 + movw r0, :lower16:_datahilo16+4 - Lpicbase2 + movt r0, :upper16:_datahilo16+4 - Lpicbase2 + movw r0, :lower16:_datahilo16alt - Lpicbase2 + movt r0, :upper16:_datahilo16alt - Lpicbase2 + movw r0, :lower16:_datahilo16alt+61440 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+61440 - Lpicbase2 + movw r0, :lower16:_datahilo16alt+2048 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+2048 - Lpicbase2 + movw r0, :lower16:_datahilo16alt+1792 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+1792 - Lpicbase2 + movw r0, :lower16:_datahilo16alt+165 - Lpicbase2 + movt r0, :upper16:_datahilo16alt+165 - Lpicbase2 + bx lr + + .data +_datahilo16: .long 0 +_datahilo16alt: .long 0 + +#endif #endif @@ -309,6 +417,17 @@ _test_branches: ; call external + addend bne _external+16 + + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + +_test_weak_call: + bl _test_weak + bl _test_weak+4 + #endif @@ -316,7 +435,12 @@ _test_branches: #if __i386__ .text .align 2 - + +Ltest_data: + .long 1 + .long 2 + .long 3 + .globl _test_loads _test_loads: pushl %ebp @@ -347,6 +471,10 @@ Lpicbase: # absolute lea of external + addend leal _ax+0x1900, %eax + # absolute load of _test_data with negative addend and local label + movl Ltest_data-16(%edi),%eax + movq Ltest_data-16(%edi),%mm4 + ret @@ -406,6 +534,16 @@ c_2: sub $(1), %ecx jcxz c_2 + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + +_test_weak_call: + call _test_weak + call _test_weak+1 + #endif @@ -483,19 +621,30 @@ _test_branches: jne _external+16 _byte_relocs: + # nonsense loop that creates byte branch relocation mov $100, %ecx -c_1: +c_1: loop _byte_relocs nop + .globl _test_weak + .weak_definition _test_weak +_test_weak: + nop + nop + +_test_weak_call: + call _test_weak + call _test_weak+1 + #endif # test that pointer-diff relocs are preserved .text -_test_diffs: .align 2 +_test_diffs: Llocal2: .long 0 .long Llocal2-_test_branches @@ -511,6 +660,7 @@ Llocal2: #endif _foo: nop +Lfoo: nop .align 2 _distance_from_foo: @@ -528,6 +678,10 @@ _distance_to_here: .long _foo - _distance_to_here .long _foo - _distance_to_here - 4 .long _foo - _distance_to_here - 12 + .long Lfoo - _distance_to_here +Ltohere: + .long Lfoo - Ltohere + .long Lfoo - Ltohere - 4 .long 0 @@ -545,6 +699,7 @@ L1: .quad _test_branches - _test_diffs .quad _test_branches - . .quad _test_branches - L1 .quad L1 - _prev + .quad _prev+100 - _test_branches #tests support for 32-bit absolute pointers .long _prev .long L1 @@ -570,11 +725,15 @@ _b: .long _test_calls+16 .long _external .long _external+16 + .long _test_weak + .long _test_weak+16 #elif __ppc64__ || __x86_64__ .quad _test_calls .quad _test_calls+16 .quad _external .quad _external+16 + .quad _test_weak + .quad _test_weak+16 #endif # test that reloc sizes are the same diff --git a/unit-tests/test-cases/relocs-neg-from-local/Makefile b/unit-tests/test-cases/relocs-neg-from-local/Makefile index 36fb47b..d484458 100644 --- a/unit-tests/test-cases/relocs-neg-from-local/Makefile +++ b/unit-tests/test-cases/relocs-neg-from-local/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2009 Apple Inc. All rights reserved. +# Copyright (c) 2009-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -41,10 +41,10 @@ run-other: run-i386: ${CC} ${ASMFLAGS} test.s -c -o test.o - ${OBJECTDUMP} test.o | grep "__data@0 plus 0xFFFFFFE2" | ${FAIL_IF_EMPTY} + ${OBJECTDUMP} test.o | grep "direct(anon@__data+0x00000000) + 0xFFFFFFFFFFFFFFE2" | ${FAIL_IF_EMPTY} ${LD} -arch ${ARCH} -r -keep_private_externs test.o -o test-r.o - ${OBJECTDUMP} test-r.o | grep "__data@0 plus 0xFFFFFFE2" | ${PASS_IFF_STDIN} + ${OBJECTDUMP} test-r.o | grep "direct(anon@__data+0x00000000) + 0xFFFFFFFFFFFFFFE2" | ${PASS_IFF_STDIN} clean: diff --git a/unit-tests/test-cases/sectcreate-dead_strip/Makefile b/unit-tests/test-cases/sectcreate-dead_strip/Makefile new file mode 100644 index 0000000..5b1f246 --- /dev/null +++ b/unit-tests/test-cases/sectcreate-dead_strip/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# The point of this test is a sanity check that ld +# can link a hello-world program with no errors (or crashes) +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -o main -sectcreate __MYSEG __mysect sect_content -dead_strip + size -l main | grep __MYSEG | ${FAIL_IF_EMPTY} + size -l main | grep __mysect | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main diff --git a/unit-tests/test-cases/sectcreate-dead_strip/main.c b/unit-tests/test-cases/sectcreate-dead_strip/main.c new file mode 100644 index 0000000..fc047f7 --- /dev/null +++ b/unit-tests/test-cases/sectcreate-dead_strip/main.c @@ -0,0 +1,5 @@ + +int main() +{ + return 0; +} diff --git a/unit-tests/test-cases/sectcreate-dead_strip/sect_content b/unit-tests/test-cases/sectcreate-dead_strip/sect_content new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/unit-tests/test-cases/sectcreate-dead_strip/sect_content @@ -0,0 +1 @@ +hello diff --git a/unit-tests/test-cases/segment-labels/Makefile b/unit-tests/test-cases/segment-labels/Makefile new file mode 100644 index 0000000..2a4a95b --- /dev/null +++ b/unit-tests/test-cases/segment-labels/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2009-2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld resolves the magic segment start/end symbols. +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c -dynamiclib -o libmain.dylib + ${CC} ${CCFLAGS} main.c -o main + ${CC} ${CCFLAGS} test.c -o test.preload -Wl,-preload -nostartfiles -nodefaultlibs -e _start + ${CC} ${CCFLAGS} test.c -o test.preload-pie -Wl,-preload -Wl,-pie -nostartfiles -nodefaultlibs -e _start + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f libmain.dylib main test.preload test.preload-pie diff --git a/unit-tests/test-cases/segment-labels/main.c b/unit-tests/test-cases/segment-labels/main.c new file mode 100644 index 0000000..441860f --- /dev/null +++ b/unit-tests/test-cases/segment-labels/main.c @@ -0,0 +1,50 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <stdio.h> + + +extern void* data_start __asm("segment$start$__DATA"); +extern void* data_end __asm("segment$end$__DATA"); +extern void* text_start __asm("segment$start$__TEXT"); +extern void* text_end __asm("segment$end$__TEXT"); +extern void* other_start __asm("segment$start$__OTHER"); +extern void* other_end __asm("segment$end$__OTHER"); + + +int other[100] __attribute__ ((section ("__OTHER,__my"))) = { 1, 2 }; + +int mytent[1000]; +static int mybss[1000]; + +int main() +{ + mytent[0] = 0; + mybss[0] = 0; + printf("text %p -> %p\n", &text_start, &text_end); + printf("data %p -> %p\n", &data_start, &data_end); + printf("other %p -> %p\n", &other_start, &other_end); + return 0; +} + + diff --git a/unit-tests/test-cases/segment-labels/test.c b/unit-tests/test-cases/segment-labels/test.c new file mode 100644 index 0000000..247b989 --- /dev/null +++ b/unit-tests/test-cases/segment-labels/test.c @@ -0,0 +1,12 @@ +extern char text_start[] __asm("segment$start$__TEXT"); +extern char text_end[] __asm("segment$end$__TEXT"); +extern char text_text_start[] __asm("section$start$__TEXT$__text"); + +void* a = &text_start; +void* b = &text_end; +void* c = &text_text_start; + +void start(void) +{ +} + diff --git a/unit-tests/test-cases/stabs-coalesce/Makefile b/unit-tests/test-cases/stabs-coalesce/Makefile index 6e11c59..806f406 100644 --- a/unit-tests/test-cases/stabs-coalesce/Makefile +++ b/unit-tests/test-cases/stabs-coalesce/Makefile @@ -34,18 +34,18 @@ include ${TESTROOT}/include/common.makefile run: all all: hello.o other.o - ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH} + g++-4.2 ${CCXXFLAGS} -gstabs+ -gused hello.o other.o -o stabs-hello-${ARCH} ${FAIL_IF_BAD_MACHO} stabs-hello-${ARCH} nm -ap stabs-hello-${ARCH} | grep FUN | grep _Z3fooi | wc -l > stabs-hello-foo-count echo " 1" > one ${PASS_IFF} diff stabs-hello-foo-count one hello.o : hello.cxx - ${CXX} ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ + g++-4.2 ${CCXXFLAGS} -gstabs+ -gused hello.cxx -c -o $@ ${FAIL_IF_BAD_OBJ} $@ other.o : other.cxx - ${CXX} ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ + g++-4.2 ${CCXXFLAGS} -gstabs+ -gused other.cxx -c -o $@ ${FAIL_IF_BAD_OBJ} $@ clean: diff --git a/unit-tests/test-cases/static-executable-weak-defines/Makefile b/unit-tests/test-cases/static-executable-weak-defines/Makefile index 05588b5..4e61937 100644 --- a/unit-tests/test-cases/static-executable-weak-defines/Makefile +++ b/unit-tests/test-cases/static-executable-weak-defines/Makefile @@ -29,7 +29,8 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} test.c -static -o test -e _entry -nostdlib -Wl,-new_linker + strip -S test -o test.stripped ${PASS_IFF_GOOD_MACHO} test clean: - rm -rf test + rm -rf test test.stripped diff --git a/unit-tests/test-cases/static-executable-weak-defines/test.c b/unit-tests/test-cases/static-executable-weak-defines/test.c index 3e03861..687cb3d 100644 --- a/unit-tests/test-cases/static-executable-weak-defines/test.c +++ b/unit-tests/test-cases/static-executable-weak-defines/test.c @@ -11,3 +11,6 @@ int entry() { return foo(); } + +// pointer to weak function might trigger external relocation +void* pfoo = &foo; diff --git a/unit-tests/test-cases/static-executable/test.c b/unit-tests/test-cases/static-executable/test.c index 27fe88d..57b349a 100644 --- a/unit-tests/test-cases/static-executable/test.c +++ b/unit-tests/test-cases/static-executable/test.c @@ -1,7 +1,10 @@ +int a; +int b = 5; + int foo() { - return 0; + return a+b; } @@ -9,3 +12,5 @@ int entry() { return foo(); } + + diff --git a/unit-tests/test-cases/symbol-hiding-umbrella/Makefile b/unit-tests/test-cases/symbol-hiding-umbrella/Makefile new file mode 100644 index 0000000..3fc3aa0 --- /dev/null +++ b/unit-tests/test-cases/symbol-hiding-umbrella/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test magic $ld$hide works with public sub library +# <rdar://problem/8388362> RedGarnet's linker does not honor $ld$hide for umbrella libraries +# + + +run: all + +all: + # In this test case aaa and bbb both moved between libfoo and libar + # between 10.4 and 10.5. + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -Wl,-reexport_library,libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar_alt.dylib + ${CC} ${CCFLAGS} main.c -o main-10.5 libfoo.dylib -L. -mmacosx-version-min=10.5 + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -o main-10.6 libfoo.dylib -L. -mmacosx-version-min=10.6 2>/dev/null + ${CC} ${CCFLAGS} main.c -o main-10.7 libfoo.dylib -L. -mmacosx-version-min=10.7 + ${PASS_IFF_GOOD_MACHO} main-10.7 + + +clean: + + rm -rf libbar.dylib libfoo.dylib main-10.5 main-10.6 main-10.7 diff --git a/unit-tests/test-cases/symbol-hiding-umbrella/bar.c b/unit-tests/test-cases/symbol-hiding-umbrella/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/unit-tests/test-cases/symbol-hiding-umbrella/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/unit-tests/test-cases/symbol-hiding-umbrella/foo.c b/unit-tests/test-cases/symbol-hiding-umbrella/foo.c new file mode 100644 index 0000000..d946cb4 --- /dev/null +++ b/unit-tests/test-cases/symbol-hiding-umbrella/foo.c @@ -0,0 +1,7 @@ + +void foo() {} + +#define SYMBOL_NOT_HERE_IN_10_6(sym) \ + extern const char sym##_tmp __asm("$ld$hide$os10.6$_" #sym ); const char sym##_tmp = 0; + +SYMBOL_NOT_HERE_IN_10_6(bar) diff --git a/unit-tests/test-cases/symbol-hiding-umbrella/main.c b/unit-tests/test-cases/symbol-hiding-umbrella/main.c new file mode 100644 index 0000000..8c9c95c --- /dev/null +++ b/unit-tests/test-cases/symbol-hiding-umbrella/main.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); + + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/unit-tests/test-cases/symbol-resolver-basic/Makefile b/unit-tests/test-cases/symbol-resolver-basic/Makefile new file mode 100644 index 0000000..15cb555 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-basic/Makefile @@ -0,0 +1,29 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test a simple symbol resolver function +# +TARGET = all +ifeq (${ARCH},ppc) + TARGET = all-ppc +endif + + +run: ${TARGET} + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${CC} ${CCFLAGS} foo.o -dynamiclib -o libfoo.dylib + ${DYLDINFO} -export libfoo.dylib | grep _foo | grep resolver | ${FAIL_IF_EMPTY} + ${LD} -arch ${ARCH} -r foo.o -o foo-r.o + ${CC} ${CCFLAGS} foo-r.o -dynamiclib -o libfoo-r.dylib + ${DYLDINFO} -export libfoo-r.dylib | grep _foo | grep resolver | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo-r.dylib + +all-ppc: + echo "PASS" + +clean: + rm -f foo.o libfoo.dylib foo-r.o libfoo-r.dylib diff --git a/unit-tests/test-cases/symbol-resolver-basic/foo.c b/unit-tests/test-cases/symbol-resolver-basic/foo.c new file mode 100644 index 0000000..c6a39d9 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-basic/foo.c @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +int foo_real() +{ + return 10; +} + +// This foo is a "resolver" function that return the actual address of "foo" +void* foo() +{ + __asm__(".desc _foo, 0x100"); + return &foo_real; +} + diff --git a/unit-tests/test-cases/symbol-resolver-hidden/Makefile b/unit-tests/test-cases/symbol-resolver-hidden/Makefile new file mode 100644 index 0000000..754dad4 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-hidden/Makefile @@ -0,0 +1,25 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test a simple symbol resolver function +# +TARGET = all +ifeq (${ARCH},ppc) + TARGET = all-ppc +endif + + +run: ${TARGET} + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-w + ${DYLDINFO} -lazy_bind libfoo.dylib | grep _a | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +all-ppc: + echo "PASS" + +clean: + rm -f libfoo.dylib diff --git a/unit-tests/test-cases/symbol-resolver-hidden/foo.c b/unit-tests/test-cases/symbol-resolver-hidden/foo.c new file mode 100644 index 0000000..c8e29e3 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-hidden/foo.c @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <stdio.h> + +extern void a(); + +void b() { + a(); +} + +static void a_impl() { + printf("Hello World!\n"); +} + +void *a_chooser() __asm__("_a") __attribute__((visibility("hidden"), noinline)); +void *a_chooser() { + __asm__(".symbol_resolver _a"); + return a_impl; +} + diff --git a/unit-tests/test-cases/tentative-to-real-r/Makefile b/unit-tests/test-cases/tentative-to-real-r/Makefile index df0ae07..6a7d3a6 100644 --- a/unit-tests/test-cases/tentative-to-real-r/Makefile +++ b/unit-tests/test-cases/tentative-to-real-r/Makefile @@ -32,10 +32,10 @@ run: all all: ${CC} ${CCFLAGS} test.c -c -o test.${ARCH}.o - ${OBJECTDUMP} -no_content test.${ARCH}.o | grep -v kind: | grep -v section: > test.${ARCH}.o.dump + ${OBJECTDUMP} -no_content -no_definition -no_section -no_combine test.${ARCH}.o > test.${ARCH}.o.dump ${LD} -arch ${ARCH} -r -d test.${ARCH}.o -o test-r-d.${ARCH}.o - ${OBJECTDUMP} -no_content test-r-d.${ARCH}.o | grep -v kind: | grep -v section: > test-r-d.${ARCH}.o.dump + ${OBJECTDUMP} -no_content -no_definition -no_section -no_combine test-r-d.${ARCH}.o > test-r-d.${ARCH}.o.dump ${PASS_IFF} diff test.${ARCH}.o.dump test-r-d.${ARCH}.o.dump diff --git a/unit-tests/test-cases/tlv-basic/Makefile b/unit-tests/test-cases/tlv-basic/Makefile new file mode 100644 index 0000000..f5be591 --- /dev/null +++ b/unit-tests/test-cases/tlv-basic/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that main executable can use TLVs +# + +run: all + +all: + ${CC} ${CCFLAGS} main.c get.s -o main + otool -lv main | grep S_THREAD_LOCAL_VARIABLES | ${FAIL_IF_EMPTY} + otool -lv main | grep S_THREAD_LOCAL_REGULAR | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm main diff --git a/unit-tests/test-cases/tlv-basic/get.s b/unit-tests/test-cases/tlv-basic/get.s new file mode 100644 index 0000000..9255841 --- /dev/null +++ b/unit-tests/test-cases/tlv-basic/get.s @@ -0,0 +1,162 @@ + + # _a is zerofill global TLV + .tbss _a$tlv$init,4,2 + + # _b is an initialized global TLV + .tdata +_b$tlv$init: + .long 5 + + # _c is zerofill non-external TLV + .tbss _c$tlv$init,4,2 + + # _d is an initialized non-external TLV + .tdata +_d$tlv$init: + .long 5 + +#if __x86_64__ + + # _a is global TLV + .tlv + .globl _a +_a: .quad __tlv_bootstrap + .quad 0 + .quad _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .quad __tlv_bootstrap + .quad 0 + .quad _b$tlv$init + + # _c is a non-external TLV + .tlv +_c: .quad __tlv_bootstrap + .quad 0 + .quad _c$tlv$init + + # _d is a non-external TLV + .tlv +_d: .quad __tlv_bootstrap + .quad 0 + .quad _d$tlv$init + + + .text + .globl _get_a +_get_a: + pushq %rbp + movq %rsp, %rbp + movq _a@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + + .globl _get_b +_get_b: + pushq %rbp + movq %rsp, %rbp + movq _b@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + + .globl _get_c +_get_c: + pushq %rbp + movq %rsp, %rbp + movq _c@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + + .globl _get_d +_get_d: + pushq %rbp + movq %rsp, %rbp + movq _d@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + +#endif + +#if __i386__ + + # _a is global TLV + .tlv + .globl _a +_a: .long __tlv_bootstrap + .long 0 + .long _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .long __tlv_bootstrap + .long 0 + .long _b$tlv$init + + # _c is a non-external TLV + .tlv +_c: .long __tlv_bootstrap + .long 0 + .long _c$tlv$init + + # _d is a non-external TLV + .tlv +_d: .long __tlv_bootstrap + .long 0 + .long _d$tlv$init + + + .text + .globl _get_a +_get_a: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _a@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + + .globl _get_b +_get_b: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _b@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + + .globl _get_c +_get_c: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _c@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + + .globl _get_d +_get_d: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _d@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + +#endif + +.subsections_via_symbols diff --git a/unit-tests/test-cases/tlv-basic/main.c b/unit-tests/test-cases/tlv-basic/main.c new file mode 100644 index 0000000..fc56a13 --- /dev/null +++ b/unit-tests/test-cases/tlv-basic/main.c @@ -0,0 +1,39 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// work around until compiler supports __thread +extern int* get_a(); +extern int* get_b(); +extern int* get_c(); +extern int* get_d(); + +int main() +{ + get_a(); + get_b(); + get_c(); + get_d(); + return 0; +} + diff --git a/unit-tests/test-cases/tlv-dylib/Makefile b/unit-tests/test-cases/tlv-dylib/Makefile new file mode 100644 index 0000000..5017692 --- /dev/null +++ b/unit-tests/test-cases/tlv-dylib/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +SHELL = bash # use bash shell so we can redirect just stderr + +# +# Check that dylibs can export TLVs +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.s -dynamiclib -o libfoo.dylib + # check trying to access TLV _foo as regular variable is an error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c -DUSE_FOO libfoo.dylib -o main 2>/dev/null + # check trying to access regular variable _bar as TLV is an error + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main.c getbar.s libfoo.dylib -o main 2>/dev/null + # check can link with TLV _foo in dylib + ${CC} ${CCFLAGS} main.c getfoo.s libfoo.dylib -o main + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf libfoo.dylib main diff --git a/unit-tests/test-cases/tlv-dylib/foo.s b/unit-tests/test-cases/tlv-dylib/foo.s new file mode 100644 index 0000000..fd2f23d --- /dev/null +++ b/unit-tests/test-cases/tlv-dylib/foo.s @@ -0,0 +1,33 @@ + +// _foo is an exported thread local variable +// _bar is an exported regular variable + + # _a is zerofill global TLV + .tbss _a$tlv$init,4,2 + +#if __x86_64__ + .tlv + .globl _foo +_foo: .quad __tlv_bootstrap + .quad 0 + .quad _a$tlv$init + + +#endif + +#if __i386__ + .tlv + .globl _foo +_foo: .long __tlv_bootstrap + .long 0 + .long _a$tlv$init +#endif + + + .data + .globl _bar +_bar: .long 0 + + + + .subsections_via_symbols diff --git a/unit-tests/test-cases/tlv-dylib/getbar.s b/unit-tests/test-cases/tlv-dylib/getbar.s new file mode 100644 index 0000000..33b0418 --- /dev/null +++ b/unit-tests/test-cases/tlv-dylib/getbar.s @@ -0,0 +1,29 @@ + +#if __x86_64__ + .text + .globl _get_bar +_get_bar: + pushq %rbp + movq %rsp, %rbp + movq _bar@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret +#endif + + +#if __i386__ + .text + .globl _get_bar +_get_bar: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _bar@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret +#endif + +.subsections_via_symbols diff --git a/unit-tests/test-cases/tlv-dylib/getfoo.s b/unit-tests/test-cases/tlv-dylib/getfoo.s new file mode 100644 index 0000000..21db32c --- /dev/null +++ b/unit-tests/test-cases/tlv-dylib/getfoo.s @@ -0,0 +1,29 @@ + +#if __x86_64__ + .text + .globl _get_foo +_get_foo: + pushq %rbp + movq %rsp, %rbp + movq _foo@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret +#endif + + +#if __i386__ + .text + .globl _get_foo +_get_foo: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _foo@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret +#endif + +.subsections_via_symbols diff --git a/src/ld/SectCreate.h b/unit-tests/test-cases/tlv-dylib/main.c similarity index 74% rename from src/ld/SectCreate.h rename to unit-tests/test-cases/tlv-dylib/main.c index d5c7f4e..d5ec3b8 100644 --- a/src/ld/SectCreate.h +++ b/unit-tests/test-cases/tlv-dylib/main.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,22 +22,15 @@ * @APPLE_LICENSE_HEADER_END@ */ - -#ifndef __SECTCREATE__ -#define __SECTCREATE__ - - -#include "ObjectFile.h" - -namespace SectCreate { - - extern ObjectFile::Reader* MakeReader(const char* segmentName, const char* sectionName, const char* path, const uint8_t fileContent[], uint64_t fileLength); - -}; - - +#if USE_FOO + extern int foo; #endif - - +int main() +{ +#if USE_FOO + foo = 1; +#endif + return 0; +} diff --git a/unit-tests/test-cases/umbrella-dylib/Makefile b/unit-tests/test-cases/umbrella-dylib/Makefile new file mode 100644 index 0000000..b20f368 --- /dev/null +++ b/unit-tests/test-cases/umbrella-dylib/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test out building an umbrella dylib. a and c are co-dependent. b depends on c. +# + + +run: all + +all: + # build bootstrap dylib of with all code + ${CC} ${CCFLAGS} -dynamiclib a.c b.c c.c -o libBig.stub -install_name /usr/lib/libBig.dylib + # link each sub library against bootstrap to intra-Big symbols + ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libBig.stub -install_name /usr/local/lib/big/liba.dylib -umbrella Big + ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libBig.stub -install_name /usr/local/lib/big/libb.dylib -umbrella Big + ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib libBig.stub -install_name /usr/local/lib/big/libc.dylib -umbrella Big + ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib + # re-link against correct dylibs now that everything is built + ${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libc.dylib -install_name /usr/local/lib/big/liba.dylib -umbrella Big + dyldinfo -lazy_bind liba.dylib | grep c2 | grep libc | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libc.dylib -install_name /usr/local/lib/big/libb.dylib -umbrella Big + ${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib liba.dylib -install_name /usr/local/lib/big/libc.dylib -umbrella Big + dyldinfo -lazy_bind libc.dylib | grep a2 | grep liba | ${FAIL_IF_EMPTY} + ${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib + ${CC} ${CCFLAGS} main.c -o main libBig.dylib -L. + ${PASS_IFF_GOOD_MACHO} main + + +clean: + + rm -rf libBig.stub liba.dylib libb.dylib libc.dylib libBig.dylib main diff --git a/unit-tests/test-cases/umbrella-dylib/a.c b/unit-tests/test-cases/umbrella-dylib/a.c new file mode 100644 index 0000000..83ff3fc --- /dev/null +++ b/unit-tests/test-cases/umbrella-dylib/a.c @@ -0,0 +1,11 @@ + +extern void c2(); + +void a1(void) +{ + c2(); +} + +void a2(void) +{ +} diff --git a/unit-tests/test-cases/umbrella-dylib/b.c b/unit-tests/test-cases/umbrella-dylib/b.c new file mode 100644 index 0000000..cdb9dfa --- /dev/null +++ b/unit-tests/test-cases/umbrella-dylib/b.c @@ -0,0 +1,7 @@ + +extern void c1(); + +void b1(void) +{ + c1(); +} diff --git a/unit-tests/test-cases/umbrella-dylib/c.c b/unit-tests/test-cases/umbrella-dylib/c.c new file mode 100644 index 0000000..01747b6 --- /dev/null +++ b/unit-tests/test-cases/umbrella-dylib/c.c @@ -0,0 +1,11 @@ + +extern void a2(); + +void c1(void) +{ + a2(); +} + +void c2(void) +{ +} diff --git a/unit-tests/test-cases/umbrella-dylib/main.c b/unit-tests/test-cases/umbrella-dylib/main.c new file mode 100644 index 0000000..8d0b5b7 --- /dev/null +++ b/unit-tests/test-cases/umbrella-dylib/main.c @@ -0,0 +1,8 @@ +extern void c1(); +extern void a1(); +int main() +{ + a1(); + c1(); + return 0; +} diff --git a/unit-tests/test-cases/undefined-dynamic-lookup/Makefile b/unit-tests/test-cases/undefined-dynamic-lookup/Makefile index eebcc37..69b40c7 100644 --- a/unit-tests/test-cases/undefined-dynamic-lookup/Makefile +++ b/unit-tests/test-cases/undefined-dynamic-lookup/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,18 +30,31 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup + ${CC} ${CCFLAGS} main.c -o main -undefined dynamic_lookup nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} main ${CC} ${CCFLAGS} main.c -o main -Wl,-U,_foo nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN} ${FAIL_IF_BAD_MACHO} main ${CC} ${CCFLAGS} main.c -o main -flat_namespace -Wl,-U,_foo nm -m main | grep _foo | grep "dynamically looked up" | ${FAIL_IF_STDIN} - ${PASS_IFF_GOOD_MACHO} main + ${DYLDINFO} -lazy_bind -bind main | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main | grep _exit | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + + ${CC} ${CCFLAGS} main.c -bundle -o main.bundle -nodefaultlibs -undefined dynamic_lookup + nm -m main.bundle | grep _foo | grep "dynamically looked up" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main.bundle | grep _foo | grep "flat-namespace" | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind -bind main.bundle | grep _exit | grep "flat-namespace" | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main.bundle + clean: - rm main + rm -f main main.bundle diff --git a/unit-tests/test-cases/unstrippable-symbols/Makefile b/unit-tests/test-cases/unstrippable-symbols/Makefile new file mode 100644 index 0000000..39937ba --- /dev/null +++ b/unit-tests/test-cases/unstrippable-symbols/Makefile @@ -0,0 +1,17 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that a dynamically referenced symbol is always exported +# + +run: all + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _keep_global | grep "referenced dynamically" | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _keep_hidden | grep "referenced dynamically" | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + +clean: + rm libfoo.dylib diff --git a/unit-tests/test-cases/unstrippable-symbols/foo.c b/unit-tests/test-cases/unstrippable-symbols/foo.c new file mode 100644 index 0000000..140f8c6 --- /dev/null +++ b/unit-tests/test-cases/unstrippable-symbols/foo.c @@ -0,0 +1,24 @@ +#include <stddef.h> + +int keep_global = 1; +asm(".desc _keep_global, 0x10"); + +__attribute__((visibility("hidden"))) int keep_hidden = 1; +asm(".desc _keep_hidden, 0x10"); + +static int keep_static = 1; +asm(".desc _keep_static, 0x10"); + + +int lose_global = 1; + +__attribute__((visibility("hidden"))) int lose_hidden = 1; + +static int lose_static = 1; + + + +int get() +{ + return keep_global + keep_hidden + keep_static + lose_global + lose_hidden + lose_static; +} diff --git a/unit-tests/test-cases/utf16-nul/Makefile b/unit-tests/test-cases/utf16-nul/Makefile new file mode 100644 index 0000000..3d845e5 --- /dev/null +++ b/unit-tests/test-cases/utf16-nul/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2008-2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that utf16 cfstring literals are coalesced. +# There is 3 CFSTR in foo.m and 1 CFSTR in bar.m +# After coalescing and dead stripping there should be only two CFSTR in the output +# + + +all: + ${CC} ${CCFLAGS} withnul.s -c -o withnul.o + ${CC} ${CCFLAGS} other.s -c -o other.o + ${LD} -r -arch ${ARCH} withnul.o other.o -o all.o + size -l all.o | grep "__ustring): 44" | ${PASS_IFF_STDIN} + + +clean: + rm -rf withnul.o other.o all.o diff --git a/unit-tests/test-cases/utf16-nul/other.s b/unit-tests/test-cases/utf16-nul/other.s new file mode 100644 index 0000000..f78d014 --- /dev/null +++ b/unit-tests/test-cases/utf16-nul/other.s @@ -0,0 +1,12 @@ + + + .section __TEXT,__ustring + .align 1 +___utf16_string_3: + .short 0x00fc, 0x0062, 0x0065, 0x0072, 0x0000 + +___utf16_string_4: + .short 0x0073, 0x0074, 0x00fc, 0x0066, 0x0066, 0x0000 + + + diff --git a/unit-tests/test-cases/utf16-nul/withnul.s b/unit-tests/test-cases/utf16-nul/withnul.s new file mode 100644 index 0000000..a1bc7ce --- /dev/null +++ b/unit-tests/test-cases/utf16-nul/withnul.s @@ -0,0 +1,8 @@ + + + .section __TEXT,__ustring + .align 1 +___utf16_string_1: + .short 0x00fc, 0x0062, 0x0065, 0x0072, 0x0000, 0x0073, 0x0074, 0x00fc, 0x0066, 0x0066, 0x0000 + + diff --git a/unit-tests/test-cases/visibility-warning/Makefile b/unit-tests/test-cases/visibility-warning/Makefile index 43bdcd8..64b0363 100644 --- a/unit-tests/test-cases/visibility-warning/Makefile +++ b/unit-tests/test-cases/visibility-warning/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -39,18 +39,24 @@ all: ${CC} ${CCFLAGS} -c foo_weak.c -o foo_weak.o ${CC} ${CCFLAGS} -c foo.c -o foo.o ${CC} ${CCFLAGS} -c foo_hidden.c -o foo_hidden.o - # weak default and weak hidden should warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_EMPTY} - # weak hidden and strong should not warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_STDIN} - # weak default and strong hidden should not warn - ${CC} ${CCFLAGS} foo_weak.o foo_hidden.o -dynamiclib -o libfoo.dylib 2> warnings.log - grep visibility warnings.log | ${FAIL_IF_STDIN} - # weak default and weak hidden but -w should not warn - ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -w -o libfoo.dylib 2> warnings.log - cat warnings.log | ${FAIL_IF_STDIN} + # weak default and weak hidden should pick default + ${CC} ${CCFLAGS} foo_weak_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_STDIN} + # weak default and weak hidden should pick default + ${CC} ${CCFLAGS} foo_weak.o foo_weak_hidden.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_STDIN} + # weak hidden and strong should not warn and pick strong + ${CC} ${CCFLAGS} foo_weak_hidden.o foo.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_STDIN} + # weak default and strong hidden should not warn and pick hidden + ${CC} ${CCFLAGS} foo_weak.o foo_hidden.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_EMPTY} + # weak default and strong hidden should not warn and pick hidden + ${CC} ${CCFLAGS} foo_hidden.o foo_weak.o -dynamiclib -o libfoo.dylib + nm -m libfoo.dylib | grep _foo | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo | grep "private external" | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} libfoo.dylib clean: diff --git a/unit-tests/test-cases/weak-def-auto-hide/Makefile b/unit-tests/test-cases/weak-def-auto-hide/Makefile new file mode 100644 index 0000000..7b74b81 --- /dev/null +++ b/unit-tests/test-cases/weak-def-auto-hide/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2010Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# Tests weak-external and weak-can-be-hidden symbols work. +# + +run: all + +all: + # test that _my_other_weak is hidden + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} other.s -c -o other.o + ${CC} ${CCFLAGS} main.o other.o -o main + nm -m main | grep _my_weak | grep "weak external" | ${FAIL_IF_EMPTY} + nm -m main | grep _my_other_weak | grep "non-external" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main + # test that .exp file can override auto-hide + ${CC} ${CCFLAGS} main.o other.o -o main2 -Wl,-exported_symbol,_my_other_weak + nm -m main2 | grep _my_weak | grep "non-external" | ${FAIL_IF_EMPTY} + nm -m main2 | grep _my_other_weak | grep "weak external" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main2 + ${CC} ${CCFLAGS} main.o other.o -o main2 -Wl,-exported_symbol,_main + nm -m main2 | grep _my_weak | grep "non-external" | ${FAIL_IF_EMPTY} + nm -m main2 | grep _my_other_weak | grep "non-external" | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} main2 + # test that auto-hide bit survives ld -r + ${LD} -r -arch ${ARCH} other.o -o other-r.o + ${OBJECTDUMP} other.o > other.o.dump + ${OBJECTDUMP} other-r.o > other-r.o.dump + ${PASS_IFF} diff other.o.dump other-r.o.dump + +clean: + rm -f main.o other.o main main2 other-r.o other.o.dump other-r.o.dump diff --git a/unit-tests/test-cases/weak-def-auto-hide/main.c b/unit-tests/test-cases/weak-def-auto-hide/main.c new file mode 100644 index 0000000..dada6bd --- /dev/null +++ b/unit-tests/test-cases/weak-def-auto-hide/main.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +void __attribute__((weak)) my_weak() +{ +} + +extern void my_other_weak(); + +int main() +{ + my_weak(); + my_other_weak(); + return 0; +} + diff --git a/unit-tests/test-cases/weak-def-auto-hide/other.s b/unit-tests/test-cases/weak-def-auto-hide/other.s new file mode 100644 index 0000000..5fd7ec4 --- /dev/null +++ b/unit-tests/test-cases/weak-def-auto-hide/other.s @@ -0,0 +1,15 @@ + + + .text + + .globl _my_weak + .weak_def_can_be_hidden _my_weak +_my_weak: nop + nop + + + .globl _my_other_weak + .weak_def_can_be_hidden _my_other_weak +_my_other_weak: nop + nop + diff --git a/unit-tests/test-cases/weak-def-flag/Makefile b/unit-tests/test-cases/weak-def-flag/Makefile index e813f49..a8d30a9 100644 --- a/unit-tests/test-cases/weak-def-flag/Makefile +++ b/unit-tests/test-cases/weak-def-flag/Makefile @@ -45,4 +45,4 @@ all: ${PASS_IFF_GOOD_MACHO} main clean: - rm main main-strip-weak \ No newline at end of file + rm -f main main-strip-weak \ No newline at end of file diff --git a/unit-tests/test-cases/weak-force/Makefile b/unit-tests/test-cases/weak-force/Makefile new file mode 100644 index 0000000..57712a6 --- /dev/null +++ b/unit-tests/test-cases/weak-force/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib -Wl,-force_symbols_weak_list foo1.exp foo.c -o libfoo.dylib + ${FAIL_IF_BAD_MACHO} libfoo.dylib + nm -m libfoo.dylib | grep _foo1 | grep weak | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _wildcheck | grep weak | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _foo3 | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _willnot | grep weak | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib -Wl,-force_symbols_not_weak_list foo2.exp foo.c -o libfoo.dylib + nm -m libfoo.dylib | grep _foo2 | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _patterncheck | grep weak | ${FAIL_IF_STDIN} + nm -m libfoo.dylib | grep _foo4 | grep weak | ${FAIL_IF_EMPTY} + nm -m libfoo.dylib | grep _patnot | grep weak | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo.dylib + + + +clean: + rm -rf libfoo.dylib diff --git a/unit-tests/test-cases/weak-force/foo.c b/unit-tests/test-cases/weak-force/foo.c new file mode 100644 index 0000000..baa5755 --- /dev/null +++ b/unit-tests/test-cases/weak-force/foo.c @@ -0,0 +1,17 @@ + + +void foo1() {} +void foo3() {} + + +__attribute__((weak)) void foo2() {} +__attribute__((weak)) void foo4() {} + + +void wildcheck() {} +void willnot() {} + + + +__attribute__((weak)) void patterncheck() {} +__attribute__((weak)) void patnot() {} diff --git a/unit-tests/test-cases/weak-force/foo1.exp b/unit-tests/test-cases/weak-force/foo1.exp new file mode 100644 index 0000000..7268896 --- /dev/null +++ b/unit-tests/test-cases/weak-force/foo1.exp @@ -0,0 +1,2 @@ +_foo1 +_wild* diff --git a/unit-tests/test-cases/weak-force/foo2.exp b/unit-tests/test-cases/weak-force/foo2.exp new file mode 100644 index 0000000..1ad4c0d --- /dev/null +++ b/unit-tests/test-cases/weak-force/foo2.exp @@ -0,0 +1,2 @@ +_foo2 +_pattern* diff --git a/unit-tests/test-cases/weak_import-force/main.c b/unit-tests/test-cases/weak_import-force/main.c index 3b9cdff..76c68ba 100644 --- a/unit-tests/test-cases/weak_import-force/main.c +++ b/unit-tests/test-cases/weak_import-force/main.c @@ -15,15 +15,18 @@ extern int bar_data2; int* pfoo = &foo_data1; int* pbar = &bar_data1; +void* pfoo1; +void* pbar1; int main (void) { // make non-lazy reference to foo1 and bar1 - if ( &foo1 == &bar1 ) { - // make lazy reference to foo2 and bar2 - foo2(); - bar2(); - } + pfoo1 = &foo1; + pbar1 = &bar1; + + // make lazy reference to foo2 and bar2 + foo2(); + bar2(); // make non-lazy reference to foo_data2 and bar_data2 return *pfoo + *pbar + foo_data2 + bar_data2; diff --git a/unit-tests/test-cases/weak_import-local/Makefile b/unit-tests/test-cases/weak_import-local/Makefile new file mode 100644 index 0000000..4d21410 --- /dev/null +++ b/unit-tests/test-cases/weak_import-local/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2010 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that setting weak_import on symbols in a linkage unit works +# + + +run: all + +all: + ${CC} ${CCFLAGS} main.c foo.c -o main + ${FAIL_IF_BAD_MACHO} main + ${CC} ${CCFLAGS} main.c foo.c -o main -mmacosx-version-min=10.4 + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main diff --git a/unit-tests/test-cases/weak_import-local/foo.c b/unit-tests/test-cases/weak_import-local/foo.c new file mode 100644 index 0000000..6eabcc7 --- /dev/null +++ b/unit-tests/test-cases/weak_import-local/foo.c @@ -0,0 +1,7 @@ + + +#include "foo.h" + +void func2() {} +int data2 = 0; // weak_import initialized + diff --git a/unit-tests/test-cases/weak_import-local/foo.h b/unit-tests/test-cases/weak_import-local/foo.h new file mode 100644 index 0000000..1dd4e82 --- /dev/null +++ b/unit-tests/test-cases/weak_import-local/foo.h @@ -0,0 +1,6 @@ + + +extern void func2() __attribute__((weak_import)); + +extern int data2 __attribute__((weak_import)); + diff --git a/unit-tests/test-cases/weak_import-local/main.c b/unit-tests/test-cases/weak_import-local/main.c new file mode 100644 index 0000000..ce044ad --- /dev/null +++ b/unit-tests/test-cases/weak_import-local/main.c @@ -0,0 +1,18 @@ +#include <stddef.h> + +#include "foo.h" + +void* pf2 = &func2; +int* pd2 = &data2; + +int main (void) +{ + if ( &func2 != NULL ) + func2(); + + if ( &data2 != NULL ) + data2 = 1; + + return 0; +} + diff --git a/unit-tests/test-cases/weak_import/main.c b/unit-tests/test-cases/weak_import/main.c index 3266aed..a08ed86 100644 --- a/unit-tests/test-cases/weak_import/main.c +++ b/unit-tests/test-cases/weak_import/main.c @@ -1,3 +1,4 @@ +#include <stddef.h> #include "foo.h" @@ -5,12 +6,14 @@ int* pdata5 = &data5; int* pdata6 = &data6; +void* pf3; int main (void) { // make non-lazy reference to func3 and func4 - if ( &func3 == &func4 ) { - // make lazy reference to func3 and func4 + pf3 = &func3; + if ( &func4 == NULL ) { + // make lazy reference to func1 and func2 func1(); func2(); } diff --git a/unit-tests/test-cases/zero-fill2/test.c b/unit-tests/test-cases/zero-fill2/test.c index 219cdc2..94bf64d 100644 --- a/unit-tests/test-cases/zero-fill2/test.c +++ b/unit-tests/test-cases/zero-fill2/test.c @@ -37,14 +37,17 @@ int bigarray2[2560]; int bigarray3[25600]; int bigarray4[256000]; int bigarray5[2560000]; +#ifndef __arm__ int bigarray6[256000000*BOOST]; +#endif static int staticbigarray1[256]; static int staticbigarray2[2560]; static int staticbigarray3[25600]; static int staticbigarray4[256000]; static int staticbigarray5[2560000]; +#ifndef __arm__ static int staticbigarray6[25600000*BOOST]; - +#endif int main() { staticbigarray1[10] = 4; @@ -52,7 +55,9 @@ int main() staticbigarray3[10] = 4; staticbigarray4[10] = 4; staticbigarray5[10] = 4; +#ifndef __arm__ staticbigarray6[10] = 4; +#endif return 0; } -- 2.45.2