From f80fe69f3f29962e8aa43a99f8ed9201548f3d78 Mon Sep 17 00:00:00 2001 From: Apple Date: Mon, 22 Jul 2013 23:37:47 +0000 Subject: [PATCH] ld64-224.1.tar.gz --- doc/man/man1/ld.1 | 8 +- ld64.xcodeproj/project.pbxproj | 22 +- src/abstraction/MachOFileAbstraction.hpp | 253 ++++- src/create_configure | 6 +- src/ld/Architectures.hpp | 5 + src/ld/HeaderAndLoadCommands.hpp | 99 +- src/ld/InputFiles.cpp | 171 +++- src/ld/InputFiles.h | 9 +- src/ld/LinkEdit.hpp | 50 +- src/ld/LinkEditClassic.hpp | 206 ++++ src/ld/Options.cpp | 370 ++++++- src/ld/Options.h | 53 +- src/ld/OutputFile.cpp | 455 ++++++++- src/ld/OutputFile.h | 10 +- src/ld/Resolver.cpp | 106 +- src/ld/Resolver.h | 3 +- src/ld/SymbolTable.cpp | 13 + src/ld/dwarf2.h | 8 +- src/ld/ld.cpp | 12 +- src/ld/ld.hpp | 104 +- src/ld/parsers/archive_file.cpp | 13 +- .../parsers/libunwind/DwarfInstructions.hpp | 306 +++++- src/ld/parsers/libunwind/DwarfParser.hpp | 2 +- src/ld/parsers/libunwind/Registers.hpp | 281 +++++- src/ld/parsers/lto_file.cpp | 35 +- src/ld/parsers/lto_file.h | 3 + src/ld/parsers/macho_dylib_file.cpp | 183 +++- src/ld/parsers/macho_dylib_file.h | 6 + src/ld/parsers/macho_relocatable_file.cpp | 914 +++++++++++++++--- src/ld/parsers/macho_relocatable_file.h | 4 +- src/ld/passes/compact_unwind.cpp | 68 +- src/ld/passes/dtrace_dof.cpp | 8 + src/ld/passes/got.cpp | 64 +- src/ld/passes/objc.cpp | 19 +- src/ld/passes/order.cpp | 34 +- src/ld/passes/stubs/stub_arm64.hpp | 390 ++++++++ src/ld/passes/stubs/stubs.cpp | 18 +- src/other/ObjectDump.cpp | 65 +- src/other/PruneTrie.cpp | 13 + src/other/dyldinfo.cpp | 75 +- src/other/machochecker.cpp | 72 +- src/other/unwinddump.cpp | 142 ++- unit-tests/include/common.makefile | 11 + unit-tests/test-cases/coalesce-force/Makefile | 45 + unit-tests/test-cases/coalesce-force/foo.c | 21 + unit-tests/test-cases/coalesce-force/foo.exp | 2 + unit-tests/test-cases/data-in-code/Makefile | 9 +- unit-tests/test-cases/data-in-code/main.c | 49 - unit-tests/test-cases/data-in-code/test.s | 14 +- .../linker_options-framework/Makefile | 43 + .../test-cases/linker_options-framework/foo.c | 3 + .../linker_options-framework/main.c | 8 + .../linker_options-library/Makefile | 44 + .../test-cases/linker_options-library/bar.c | 1 + .../test-cases/linker_options-library/foo.c | 3 + .../test-cases/linker_options-library/main.c | 10 + .../test-cases/lto-dynamic_export/Makefile | 42 + .../test-cases/lto-dynamic_export/main.c | 14 + .../test-cases/order_file-archive/Makefile | 43 + .../test-cases/order_file-archive/foo.c | 4 + .../test-cases/order_file-archive/main.c | 33 + .../order_file-archive/main.expected | 4 + .../test-cases/order_file-archive/main.order | 5 + .../test-cases/re-export-symbol/Makefile | 6 +- unit-tests/test-cases/re-export-symbol/foo.c | 19 +- 65 files changed, 4644 insertions(+), 467 deletions(-) create mode 100644 src/ld/passes/stubs/stub_arm64.hpp create mode 100644 unit-tests/test-cases/coalesce-force/Makefile create mode 100644 unit-tests/test-cases/coalesce-force/foo.c create mode 100644 unit-tests/test-cases/coalesce-force/foo.exp delete mode 100644 unit-tests/test-cases/data-in-code/main.c create mode 100644 unit-tests/test-cases/linker_options-framework/Makefile create mode 100644 unit-tests/test-cases/linker_options-framework/foo.c create mode 100644 unit-tests/test-cases/linker_options-framework/main.c create mode 100644 unit-tests/test-cases/linker_options-library/Makefile create mode 100644 unit-tests/test-cases/linker_options-library/bar.c create mode 100644 unit-tests/test-cases/linker_options-library/foo.c create mode 100644 unit-tests/test-cases/linker_options-library/main.c create mode 100644 unit-tests/test-cases/lto-dynamic_export/Makefile create mode 100644 unit-tests/test-cases/lto-dynamic_export/main.c create mode 100644 unit-tests/test-cases/order_file-archive/Makefile create mode 100644 unit-tests/test-cases/order_file-archive/foo.c create mode 100644 unit-tests/test-cases/order_file-archive/main.c create mode 100644 unit-tests/test-cases/order_file-archive/main.expected create mode 100644 unit-tests/test-cases/order_file-archive/main.order diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index c2eb01d..aef8eb9 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -357,6 +357,10 @@ is a hexadecimal number with an optional leading 0x. The should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero. .It Fl allow_stack_execute Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. +.It Fl export_dynamic +Preserves all global symbols in main executables during LTO. Without this option, Link Time Optimization +is allowed to inline and remove global functions. This option is used when a main executable may load +a plug-in which requires certain symbols from the main executable. .El .Ss Options when creating a bundle .Bl -tag @@ -695,7 +699,7 @@ order file to cluster commonly used/dirty globals onto the same page(s). .Ss Obsolete Options .Bl -tag .It Fl segalign Ar value -All segments must be page aligned. This option is obsolete. +All segments must be page aligned. .It Fl seglinkedit Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete. .It Fl noseglinkedit @@ -703,7 +707,7 @@ This is the default. This option is obsolete. .It Fl fvmlib Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. .It Fl preload -Preload executables (MH_PRELOAD) are no longer supported. This option is obsolete. +Preload executables (MH_PRELOAD) are no longer supported. .It Fl sectobjectsymbols Ar segname Ar sectname Adding a local label at a section start is no longer supported. This option is obsolete. .It Fl nofixprebinding diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index dd74668..4ffd5d8 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -87,7 +87,7 @@ /* Begin PBXBuildRule section */ F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.llvm.clang.1_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.c; isEditable = 1; outputFiles = ( @@ -248,8 +248,9 @@ F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F93A9BEC12C2E51900BAA11D /* stub_arm64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm64.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F93CB246116E69EB003233B8 /* tlvp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tlvp.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F93CB247116E69EB003233B8 /* tlvp.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = tlvp.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -312,8 +313,8 @@ F9BA955D10A233000097A440 /* huge.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = huge.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9BA963310A2545C0097A440 /* compact_unwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_unwind.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9BA963410A2545C0097A440 /* compact_unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; @@ -436,6 +437,7 @@ F9AA65101051BD2B003E3539 /* stubs.cpp */, F9AA650F1051BD2B003E3539 /* stub_arm.hpp */, F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */, + F93A9BEC12C2E51900BAA11D /* stub_arm64.hpp */, F9BA8A7F1096150F0097A440 /* stub_x86.hpp */, F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */, F989D0391062E6350014B60C /* stub_x86_64.hpp */, @@ -1479,6 +1481,10 @@ GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; @@ -1504,6 +1510,10 @@ GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; @@ -1519,6 +1529,10 @@ GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index 4fd6123..60b4f10 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "FileAbstraction.hpp" @@ -205,7 +206,7 @@ #ifndef LC_DATA_IN_CODE #define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */ - struct data_in_code_entry { + struct data_in_code_entry { uint32_t offset; uint16_t length; uint16_t kind; @@ -216,6 +217,20 @@ #define LC_DYLIB_CODE_SIGN_DRS 0x2B #endif +#ifndef LC_ENCRYPTION_INFO_64 + #define LC_ENCRYPTION_INFO_64 0x2C + struct encryption_info_command_64 { + uint32_t cmd; + uint32_t cmdsize; + uint32_t cryptoff; + uint32_t cryptsize; + uint32_t cryptid; + uint32_t pad; + }; +#endif + + + #ifndef CPU_SUBTYPE_ARM_V7F #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) #endif @@ -227,6 +242,124 @@ #endif + +// hack until arm64 headers are worked out +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +#define CPU_SUBTYPE_ARM64_ALL 0 +#define CPU_SUBTYPE_ARM64_V8 1 + +#define ARM64_RELOC_UNSIGNED 0 // for pointers +#define ARM64_RELOC_SUBTRACTOR 1 // must be followed by a ARM64_RELOC_UNSIGNED +#define ARM64_RELOC_BRANCH26 2 // a B/BL instruction with 26-bit displacement +#define ARM64_RELOC_PAGE21 3 // pc-rel distance to page of target +#define ARM64_RELOC_PAGEOFF12 4 // offset within page, scaled by r_length +#define ARM64_RELOC_GOT_LOAD_PAGE21 5 // pc-rel distance to page of GOT slot +#define ARM64_RELOC_GOT_LOAD_PAGEOFF12 6 // offset within page of GOT slot, scaled by r_length +#define ARM64_RELOC_POINTER_TO_GOT 7 // for pointers to GOT slots +#define ARM64_RELOC_TLVP_LOAD_PAGE21 8 // pc-rel distance to page of TLVP slot +#define ARM64_RELOC_TLVP_LOAD_PAGEOFF12 9 // offset within page of TLVP slot, scaled by r_length +#define ARM64_RELOC_ADDEND 10 // r_symbolnum is addend for next reloc + + + +#define UNW_ARM64_X0 0 +#define UNW_ARM64_X1 1 +#define UNW_ARM64_X2 2 +#define UNW_ARM64_X3 3 +#define UNW_ARM64_X4 4 +#define UNW_ARM64_X5 5 +#define UNW_ARM64_X6 6 +#define UNW_ARM64_X7 7 +#define UNW_ARM64_X8 8 +#define UNW_ARM64_X9 9 +#define UNW_ARM64_X10 10 +#define UNW_ARM64_X11 11 +#define UNW_ARM64_X12 12 +#define UNW_ARM64_X13 13 +#define UNW_ARM64_X14 14 +#define UNW_ARM64_X15 15 +#define UNW_ARM64_X16 16 +#define UNW_ARM64_X17 17 +#define UNW_ARM64_X18 18 +#define UNW_ARM64_X19 19 +#define UNW_ARM64_X20 20 +#define UNW_ARM64_X21 21 +#define UNW_ARM64_X22 22 +#define UNW_ARM64_X23 23 +#define UNW_ARM64_X24 24 +#define UNW_ARM64_X25 25 +#define UNW_ARM64_X26 26 +#define UNW_ARM64_X27 27 +#define UNW_ARM64_X28 28 +#define UNW_ARM64_X29 29 +#define UNW_ARM64_FP 29 +#define UNW_ARM64_X30 30 +#define UNW_ARM64_LR 30 +#define UNW_ARM64_X31 31 +#define UNW_ARM64_SP 31 +#define UNW_ARM64_D0 64 +#define UNW_ARM64_D1 65 +#define UNW_ARM64_D2 66 +#define UNW_ARM64_D3 67 +#define UNW_ARM64_D4 68 +#define UNW_ARM64_D5 69 +#define UNW_ARM64_D6 70 +#define UNW_ARM64_D7 71 +#define UNW_ARM64_D8 72 +#define UNW_ARM64_D9 73 +#define UNW_ARM64_D10 74 +#define UNW_ARM64_D11 75 +#define UNW_ARM64_D12 76 +#define UNW_ARM64_D13 77 +#define UNW_ARM64_D14 78 +#define UNW_ARM64_D15 79 +#define UNW_ARM64_D16 80 +#define UNW_ARM64_D17 81 +#define UNW_ARM64_D18 82 +#define UNW_ARM64_D19 83 +#define UNW_ARM64_D20 84 +#define UNW_ARM64_D21 85 +#define UNW_ARM64_D22 86 +#define UNW_ARM64_D23 87 +#define UNW_ARM64_D24 88 +#define UNW_ARM64_D25 89 +#define UNW_ARM64_D26 90 +#define UNW_ARM64_D27 91 +#define UNW_ARM64_D28 92 +#define UNW_ARM64_D29 93 +#define UNW_ARM64_D30 94 +#define UNW_ARM64_D31 95 + +#define UNWIND_ARM64_MODE_MASK 0x0F000000 +#define UNWIND_ARM64_MODE_FRAME_OLD 0x01000000 +#define UNWIND_ARM64_MODE_FRAMELESS 0x02000000 +#define UNWIND_ARM64_MODE_DWARF 0x03000000 +#define UNWIND_ARM64_MODE_FRAME 0x04000000 + +#define UNWIND_ARM64_FRAME_X19_X20_PAIR 0x00000001 +#define UNWIND_ARM64_FRAME_X21_X22_PAIR 0x00000002 +#define UNWIND_ARM64_FRAME_X23_X24_PAIR 0x00000004 +#define UNWIND_ARM64_FRAME_X25_X26_PAIR 0x00000008 +#define UNWIND_ARM64_FRAME_X27_X28_PAIR 0x00000010 +#define UNWIND_ARM64_FRAME_D8_D9_PAIR 0x00000100 +#define UNWIND_ARM64_FRAME_D10_D11_PAIR 0x00000200 +#define UNWIND_ARM64_FRAME_D12_D13_PAIR 0x00000400 +#define UNWIND_ARM64_FRAME_D14_D15_PAIR 0x00000800 + +#define UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK 0x00FFF000 + +#define UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD 0x00000001 +#define UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD 0x00000002 +#define UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD 0x00000004 +#define UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD 0x00000008 +#define UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD 0x00000010 +#define UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD 0x00000020 +#define UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD 0x00000040 +#define UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD 0x00000080 + + +#define UNWIND_ARM64_DWARF_SECTION_OFFSET 0x00FFFFFF + #ifndef LC_SOURCE_VERSION #define LC_SOURCE_VERSION 0x2A struct source_version_command { @@ -250,6 +383,38 @@ #define LC_DYLIB_CODE_SIGN_DRS 0x2B #endif +#ifndef LC_LINKER_OPTION + #define LC_LINKER_OPTION 0x2D + + struct linker_option_command { + uint32_t cmd; /*LC_LINKER_OPTION only used in MH_OBJECT filetypes */ + uint32_t cmdsize; + uint32_t count; /* number of strings */ + /* concatenation of zero terminated UTF8 strings. Zero filled at end to align */ + }; +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + + +#ifndef CPU_SUBTYPE_ARM_V8 + #define CPU_SUBTYPE_ARM_V8 ((cpu_subtype_t) 13) +#endif + +#ifndef CPU_SUBTYPE_ARM_V6M + #define CPU_SUBTYPE_ARM_V6M ((cpu_subtype_t) 14) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7M + #define CPU_SUBTYPE_ARM_V7M ((cpu_subtype_t) 15) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7EM + #define CPU_SUBTYPE_ARM_V7EM ((cpu_subtype_t) 16) +#endif + struct ArchInfo { const char* archName; @@ -295,11 +460,33 @@ static const ArchInfo archInfoArray[] = { #if SUPPORT_ARCH_armv7s { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S, "thumbv7s-", "armv7s", true, true }, #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv6m + { "armv6m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6M, "thumbv6m-", "", true, false }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7m + { "armv7m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7M, "thumbv7m-", "armv7m", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7em + { "armv7em", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7EM, "thumbv7em-", "armv7em", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv8 + { "armv8", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V8, "thumbv8-", "armv8", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_arm64 + { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, "arm64-", "", false, false }, +#endif +#if SUPPORT_ARCH_arm64v8 + { "arm64v8", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8, "arm64v8-", "", true, false }, #endif { NULL, 0, 0, NULL, NULL, false, false } }; - + // weird, but this include must wait until after SUPPORT_ARCH_arm_any is set up #if SUPPORT_ARCH_arm_any #include @@ -1125,27 +1312,37 @@ private: // // mach-o encyrption info load command // +template struct macho_encryption_info_content {}; +template <> struct macho_encryption_info_content > { struct encryption_info_command fields; }; +template <> struct macho_encryption_info_content > { struct encryption_info_command_64 fields; }; +template <> struct macho_encryption_info_content > { struct encryption_info_command fields; }; +template <> struct macho_encryption_info_content > { struct encryption_info_command_64 fields; }; + + template class macho_encryption_info_command { public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + uint32_t cmd() const INLINE { return E::get32(entry.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(entry.fields.cmd, value); } - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + uint32_t cmdsize() const INLINE { return E::get32(entry.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(entry.fields.cmdsize, value); } - uint32_t cryptoff() const INLINE { return E::get32(fields.cryptoff); } - void set_cryptoff(uint32_t value) INLINE { E::set32(fields.cryptoff, value); } + uint32_t cryptoff() const INLINE { return E::get32(entry.fields.cryptoff); } + void set_cryptoff(uint32_t value) INLINE { E::set32(entry.fields.cryptoff, value); } - uint32_t cryptsize() const INLINE { return E::get32(fields.cryptsize); } - void set_cryptsize(uint32_t value) INLINE { E::set32(fields.cryptsize, value); } + uint32_t cryptsize() const INLINE { return E::get32(entry.fields.cryptsize); } + void set_cryptsize(uint32_t value) INLINE { E::set32(entry.fields.cryptsize, value); } - uint32_t cryptid() const INLINE { return E::get32(fields.cryptid); } - void set_cryptid(uint32_t value) INLINE { E::set32(fields.cryptid, value); } + uint32_t cryptid() const INLINE { return E::get32(entry.fields.cryptid); } + void set_cryptid(uint32_t value) INLINE { E::set32(entry.fields.cryptid, value); } + uint32_t pad() const INLINE { return E::get32(entry.fields.pad); } + void set_pad(uint32_t value) INLINE { E::set32(entry.fields.pad, value); } + typedef typename P::E E; private: - encryption_info_command fields; + macho_encryption_info_content

entry; }; @@ -1466,6 +1663,36 @@ private: data_in_code_entry fields; }; +#ifndef DICE_KIND_DATA + #define DICE_KIND_DATA 0x0001 + #define DICE_KIND_JUMP_TABLE8 0x0002 + #define DICE_KIND_JUMP_TABLE16 0x0003 + #define DICE_KIND_JUMP_TABLE32 0x0004 + #define DICE_KIND_ABS_JUMP_TABLE32 0x0005 +#endif + +template +class macho_linker_option_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint64_t count() const INLINE { return fields.count; } + void set_count(uint32_t value) INLINE { E::set32(fields.count, value); } + + const char* buffer() const INLINE { return ((char*)&fields) + sizeof(linker_option_command); } + char* buffer() INLINE { return ((char*)&fields) + sizeof(linker_option_command); } + + typedef typename P::E E; +private: + linker_option_command fields; +}; + + + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/src/create_configure b/src/create_configure index 447ec7a..aed4fa3 100755 --- a/src/create_configure +++ b/src/create_configure @@ -11,17 +11,17 @@ else fi if [ -z "${RC_SUPPORTED_ARCHS}" ]; then - RC_SUPPORTED_ARCHS="i386 x86_64 armv7 armv7s" + RC_SUPPORTED_ARCHS="i386 x86_64 armv7 armv7s arm64" fi for ANARCH in ${RC_SUPPORTED_ARCHS} do - KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,i386,x86_64," + KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64," FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"` if [ $FOUND ]; then echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h else - echo "#error uknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h + echo "#error unknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h fi done diff --git a/src/ld/Architectures.hpp b/src/ld/Architectures.hpp index 1145550..fdf795b 100644 --- a/src/ld/Architectures.hpp +++ b/src/ld/Architectures.hpp @@ -56,6 +56,11 @@ struct arm typedef Pointer32 P; }; +struct arm64 +{ + typedef Pointer64 P; +}; + #endif // __ARCHITECTURES__ diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index 0885715..35daef1 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -109,7 +109,8 @@ private: uint8_t* copyDataInCodeLoadCommand(uint8_t* p) const; uint8_t* copyDependentDRLoadCommand(uint8_t* p) const; uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const; - + uint8_t* copyLinkerOptionsLoadCommand(uint8_t* p, const std::vector&) const; + uint32_t sectionFlags(ld::Internal::FinalSection* sect) const; bool sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const; @@ -143,6 +144,7 @@ private: std::vector _subUmbrellaNames; uint8_t _uuid[16]; mutable macho_uuid_command

* _uuidCmdInOutputBuffer; + std::vector< std::vector > _linkerOptions; static ld::Section _s_section; static ld::Section _s_preload_section; @@ -192,6 +194,22 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: break; } } + for (CStringSet::const_iterator it = _state.linkerOptionFrameworks.begin(); it != _state.linkerOptionFrameworks.end(); ++it) { + const char* frameWorkName = *it; + std::vector* lo = new std::vector(); + lo->push_back("-framework"); + lo->push_back(frameWorkName); + _linkerOptions.push_back(*lo); + }; + for (CStringSet::const_iterator it = _state.linkerOptionLibraries.begin(); it != _state.linkerOptionLibraries.end(); ++it) { + const char* libName = *it; + std::vector* lo = new std::vector(); + char * s = new char[strlen(libName)+3]; + strcpy(s, "-l"); + strcat(s, libName); + lo->push_back(s); + _linkerOptions.push_back(*lo); + }; break; case Options::kStaticExecutable: _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); @@ -210,6 +228,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _dylibLoadCommmandsCount = _writer.dylibCount(); _allowableClientLoadCommmandsCount = _options.allowableClients().size(); _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); + if ( ! _options.useSimplifiedDylibReExports() ) { // target OS does not support LC_REEXPORT_DYLIB, so use old complicated load commands for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { @@ -394,6 +413,17 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasDataInCodeLoadCommand ) sz += sizeof(macho_linkedit_data_command

); + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + uint32_t s = sizeof(macho_linker_option_command

); + const std::vector& options = *it; + for (std::vector::const_iterator t=options.begin(); t != options.end(); ++t) { + s += (strlen(*t) + 1); + } + sz += alignedSize(s); + } + } + if ( _hasDependentDRInfo ) sz += sizeof(macho_linkedit_data_command

); @@ -465,6 +495,12 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasDataInCodeLoadCommand ) ++count; + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + ++count; + } + } + if ( _hasDependentDRInfo ) ++count; @@ -556,10 +592,12 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_I386; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_X86_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM64; } @@ -584,6 +622,12 @@ uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const return _state.cpuSubType; } +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + return CPU_SUBTYPE_ARM64_ALL; +} + template @@ -1144,6 +1188,28 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) cons } +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 34 * 8); // base size + ARM_EXCEPTION_STATE64_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command

* cmd = (macho_thread_command

*)p; + cmd->set_cmd(LC_UNIXTHREAD); + cmd->set_cmdsize(threadLoadCommandSize()); + cmd->set_flavor(6); // ARM_THREAD_STATE64 + cmd->set_count(68); // ARM_EXCEPTION_STATE64_COUNT + cmd->set_thread_register(32, start); // pc + if ( _options.hasCustomStack() ) + cmd->set_thread_register(31, _options.customStackAddr()); // sp + return p + threadLoadCommandSize(); +} + template uint8_t* HeaderAndLoadCommandsAtom::copyEntryPointLoadCommand(uint8_t* p) const @@ -1165,7 +1231,7 @@ template uint8_t* HeaderAndLoadCommandsAtom::copyEncryptionLoadCommand(uint8_t* p) const { macho_encryption_info_command

* cmd = (macho_encryption_info_command

*)p; - cmd->set_cmd(LC_ENCRYPTION_INFO); + cmd->set_cmd(sizeof(typename A::P::uint_t) == 4 ? LC_ENCRYPTION_INFO : LC_ENCRYPTION_INFO_64); cmd->set_cmdsize(sizeof(macho_encryption_info_command

)); assert(_writer.encryptedTextStartOffset() != 0); assert(_writer.encryptedTextEndOffset() != 0); @@ -1310,6 +1376,27 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDataInCodeLoadCommand(uint8_t* p) con } +template +uint8_t* HeaderAndLoadCommandsAtom::copyLinkerOptionsLoadCommand(uint8_t* p, const std::vector& options) const +{ + macho_linker_option_command

* cmd = (macho_linker_option_command

*)p; + cmd->set_cmd(LC_LINKER_OPTION); + cmd->set_count(options.size()); + char* buffer = cmd->buffer(); + uint32_t sz = sizeof(macho_linker_option_command

); + for (std::vector::const_iterator it=options.begin(); it != options.end(); ++it) { + const char* opt = *it; + uint32_t len = strlen(opt); + strcpy(buffer, opt); + sz += (len + 1); + buffer += (len + 1); + } + sz = alignedSize(sz); + cmd->set_cmdsize(sz); + return p + sz; +} + + template uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) const { @@ -1384,7 +1471,7 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasSplitSegInfoLoadCommand ) p = this->copySplitSegInfoLoadCommand(p); - for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + for (uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { p = this->copyDylibLoadCommand(p, _writer.dylibByOrdinal(ord)); } @@ -1425,6 +1512,12 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasDataInCodeLoadCommand ) p = this->copyDataInCodeLoadCommand(p); + + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + p = this->copyLinkerOptionsLoadCommand(p, *it); + } + } if ( _hasDependentDRInfo ) p = this->copyDependentDRLoadCommand(p); diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index 7bf136b..a4b256f 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -1,3 +1,4 @@ + /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * * Copyright (c) 2009-2011 Apple Inc. All rights reserved. @@ -60,6 +61,7 @@ #include "archive_file.h" #include "lto_file.h" #include "opaque_section_file.h" +#include "MachOFileAbstraction.hpp" #include "Snapshot.h" const bool _s_logPThreads = false; @@ -180,7 +182,11 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) const char* result = mach_o::relocatable::archName(p); if ( result != NULL ) return result; - + + result = mach_o::dylib::archName(p); + if ( result != NULL ) + return result; + result = lto::archName(p, len); if ( result != NULL ) return result; @@ -192,7 +198,7 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) strcpy(unsupported, "unsupported file format ("); for (unsigned i=0; i(file); + if ( file == _bundleLoader ) { + _options.dumpDependency(Options::depBundleLoader, file->path()); + } + else if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) { + if ( indirect ) + _options.dumpDependency(Options::depUpwardIndirectDylib, file->path()); + else + _options.dumpDependency(Options::depUpwardDirectDylib, file->path()); + } + else { + if ( indirect ) + _options.dumpDependency(Options::depIndirectDylib, file->path()); + else + _options.dumpDependency(Options::depDirectDylib, file->path()); + } + } } void InputFiles::logArchive(ld::File* file) const @@ -396,7 +423,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const if ( trace_file_path != NULL ) { trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); if ( trace_file == -1 ) - throwf("Could not open or create trace file: %s", trace_file_path); + throwf("Could not open or create trace file (errno=%d): %s", errno, trace_file_path); } else { trace_file = fileno(stderr); @@ -420,6 +447,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const } } + ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath) { //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); @@ -435,6 +463,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from Options::FileInfo info = _options.findFile(dit->useInstead); _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); info.ordinal = _indirectDylibOrdinal; + info.options.fIndirectDylib = true; ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { @@ -467,6 +496,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from Options::FileInfo info = _options.findFileUsingPaths(installPath); _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); info.ordinal = _indirectDylibOrdinal; + info.options.fIndirectDylib = true; try { ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); @@ -487,18 +517,86 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from } - -void InputFiles::createIndirectDylibs() -{ - _allDirectDylibsLoaded = true; - _indirectDylibOrdinal = ld::File::Ordinal::indirectDylibBase(); - - // mark all dylibs initially specified as required and check if they can be used +// mark all dylibs initially specified as required, and check if they can be used +void InputFiles::markExplicitlyLinkedDylibs() +{ for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) { it->second->setExplicitlyLinked(); this->checkDylibClientRestrictions(it->second); } - +} + +bool InputFiles::libraryAlreadyLoaded(const char* path) +{ + for (std::vector::const_iterator it = _inputFiles.begin(); it != _inputFiles.end(); ++it) { + if ( strcmp(path, (*it)->path()) == 0 ) + return true; + } + return false; +} + + +void InputFiles::addLinkerOptionLibraries(ld::Internal& state) +{ + if ( _options.outputKind() == Options::kObjectFile ) + return; + + // process frameworks specified in .o linker options + for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) { + const char* frameworkName = *it; + Options::FileInfo info = _options.findFramework(frameworkName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + try { + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->setImplicitlyLinked(); + this->addDylib(dylibReader, info); + } + } + else { + throwf("framework linker option at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + throwf("in '%s', %s", info.path, msg); + } + } + } + // process libraries specified in .o linker options + for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) { + const char* libName = *it; + Options::FileInfo info = _options.findLibrary(libName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + try { + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + ld::archive::File* archiveReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + dylibReader->setImplicitlyLinked(); + this->addDylib(dylibReader, info); + } + else if ( archiveReader != NULL ) { + _searchLibraries.push_back(LibraryInfo(archiveReader)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depArchive, archiveReader->path()); + } + else { + throwf("linker option dylib at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + throwf("in '%s', %s", info.path, msg); + } + } + } +} + +void InputFiles::createIndirectDylibs() +{ // keep processing dylibs until no more dylibs are added unsigned long lastMapSize = 0; std::set dylibsProcessed; @@ -539,9 +637,11 @@ void InputFiles::createIndirectDylibs() void InputFiles::createOpaqueFileSections() { - // extra command line section always at end + // extra command line sections always at end for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) { _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depSection, it->path); } } @@ -672,8 +772,10 @@ InputFiles::InputFiles(Options& opts, const char** archName) : _totalObjectSize(0), _totalArchiveSize(0), _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0), _options(opts), _bundleLoader(NULL), - _allDirectDylibsLoaded(false), _inferredArch(false), - _exception(NULL) + _inferredArch(false), + _exception(NULL), + _indirectDylibOrdinal(ld::File::Ordinal::indirectDylibBase()), + _linkerOptionOrdinal(ld::File::Ordinal::linkeOptionBase()) { // fStartCreateReadersTime = mach_absolute_time(); if ( opts.architecture() == 0 ) { @@ -781,7 +883,8 @@ void InputFiles::parseWorkerThread() { if (_s_logPThreads) printf("parsing index %u\n", slot); try { file = makeFile(entry, false); - } catch (const char *msg) { + } + catch (const char *msg) { if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { if ( _options.ignoreOtherArchInputFiles() ) { // ignore, because this is about an architecture not in use @@ -789,8 +892,9 @@ void InputFiles::parseWorkerThread() { else { warning("ignoring file %s, %s", entry.path, msg); } - } else { - exception = msg; + } + else { + asprintf((char**)&exception, "%s file '%s'", msg, entry.path); } file = new IgnoredFile(entry.path, entry.modTime, entry.ordinal, ld::File::Other); } @@ -802,7 +906,8 @@ void InputFiles::parseWorkerThread() { // We are about to die, so set to zero to stop other threads from doing unneeded work. _remainingInputFiles = 0; _exception = exception; - } else { + } + else { _inputFiles[slot] = file; if (_neededFileSlot == slot) pthread_cond_signal(&_newFileAvailable); @@ -867,22 +972,23 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& } // remove warning for Same install name for CoreServices and CFNetwork? //if ( !dylibOnCommandLineTwice && !isSymlink ) - // warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); + // warning("dylibs with same install name: %p %s and %p %s", pos->second, pos->second->path(), reader, reader->path()); } } else if ( info.options.fBundleLoader ) _bundleLoader = reader; // log direct readers - if ( !_allDirectDylibsLoaded ) + if ( ! info.options.fIndirectDylib ) this->logDylib(reader, false); // update stats _totalDylibsLoaded++; // just add direct libraries to search-first list - if ( !_allDirectDylibsLoaded ) + if ( ! info.options.fIndirectDylib ) _searchLibraries.push_back(LibraryInfo(reader)); + return reader; } @@ -918,7 +1024,7 @@ void InputFiles::waitForInputFiles() if (it == fileMap.end()) throwf("pipelined linking error - not in file list: %s\n", path_buf); Options::FileInfo* inputInfo = (Options::FileInfo*)it->second; - if (!inputInfo->checkFileExists()) + if (!inputInfo->checkFileExists(_options)) throwf("pipelined linking error - file does not exist: %s\n", inputInfo->path); pthread_mutex_lock(&_parseLock); if (_idleWorkers) @@ -946,7 +1052,7 @@ void InputFiles::waitForInputFiles(InputFiles *inputFiles) { #endif -void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) +void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal& state) { // add all direct object, archives, and dylibs const std::vector& files = _options.getInputFiles(); @@ -986,6 +1092,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) { ld::relocatable::File* reloc = (ld::relocatable::File*)file; _options.snapshot().recordObjectFile(reloc->path()); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depObjectFile, reloc->path()); } break; case ld::File::Dylib: @@ -1001,6 +1109,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() ) logArchive(archive); _searchLibraries.push_back(LibraryInfo(archive)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depArchive, archive->path()); } break; case ld::File::Other: @@ -1014,6 +1124,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) file->forEachAtom(handler); } + markExplicitlyLinkedDylibs(); + addLinkerOptionLibraries(state); createIndirectDylibs(); createOpaqueFileSections(); @@ -1095,7 +1207,7 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc logArchive(archiveFile); _options.snapshot().recordArchive(archiveFile->path()); // found data definition in static library, done - return true; + return true; } } else { @@ -1145,7 +1257,7 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc bool InputFiles::searchWeakDefInDylib(const char* name) const { - // search all relevant dylibs to see if any of a weak-def with this name + // search all relevant dylibs to see if any have a weak-def with this name for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { ld::dylib::File* dylibFile = it->second; if ( dylibFile->implicitlyLinked() || dylibFile->explicitlyLinked() ) { @@ -1224,4 +1336,3 @@ void InputFiles::dylibs(ld::Internal& state) } // namespace tool } // namespace ld - diff --git a/src/ld/InputFiles.h b/src/ld/InputFiles.h index 9b969ee..3c55d45 100644 --- a/src/ld/InputFiles.h +++ b/src/ld/InputFiles.h @@ -63,7 +63,7 @@ public: virtual ld::dylib::File* findDylib(const char* installPath, const char* fromPath); // iterates all atoms in initial files - void forEachInitialAtom(ld::File::AtomHandler&); + void forEachInitialAtom(ld::File::AtomHandler&, ld::Internal& state); // searches libraries for name bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler&) const; @@ -90,12 +90,15 @@ private: void logTraceInfo (const char* format, ...) const; void logDylib(ld::File*, bool indirect); void logArchive(ld::File*) const; + void markExplicitlyLinkedDylibs(); void createIndirectDylibs(); void checkDylibClientRestrictions(ld::dylib::File*); void createOpaqueFileSections(); + void addLinkerOptionLibraries(ld::Internal& state); + bool libraryAlreadyLoaded(const char* path); // for pipelined linking - void waitForInputFiles(); + void waitForInputFiles(); static void waitForInputFiles(InputFiles *inputFiles); // for threaded input file processing @@ -111,7 +114,6 @@ private: InstallNameToDylib _installPathToDylibs; std::set _allDylibs; ld::dylib::File* _bundleLoader; - bool _allDirectDylibsLoaded; bool _inferredArch; struct strcompclass { bool operator() (const char *a, const char *b) const { return ::strcmp(a, b) < 0; } @@ -132,6 +134,7 @@ private: int _remainingInputFiles; // number of input files still to parse ld::File::Ordinal _indirectDylibOrdinal; + ld::File::Ordinal _linkerOptionOrdinal; class LibraryInfo { ld::File* _lib; diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp index 1149f0d..1b41fb2 100644 --- a/src/ld/LinkEdit.hpp +++ b/src/ld/LinkEdit.hpp @@ -1005,6 +1005,14 @@ void ExportInfoAtom::encode() const entries.push_back(entry); //fprintf(stderr, "re-export %s from lib %llu as %s\n", entry.importName, entry.other, entry.name); } + else if ( atom->definition() == ld::Atom::definitionAbsolute ) { + entry.name = atom->name(); + entry.flags = _options.canUseAbsoluteSymbols() ? EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE : EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + entry.address = address; + entry.other = other; + entry.importName = NULL; + entries.push_back(entry); + } else { if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; @@ -1068,6 +1076,7 @@ private: mutable std::vector _thumbHi16Locations[16]; mutable std::vector _armLo16Locations; mutable std::vector _armHi16Locations[16]; + mutable std::vector _adrpLocations; static ld::Section _s_section; @@ -1092,6 +1101,8 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: _32bitPointerLocations.push_back(address); break; case ld::Fixup::kindStoreLittleEndian64: @@ -1110,6 +1121,8 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki switch (kind) { case ld::Fixup::kindStoreLittleEndian32: case ld::Fixup::kindStoreTargetAddressLittleEndian32: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: _32bitPointerLocations.push_back(address); break; default: @@ -1145,7 +1158,34 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki } } - +#if SUPPORT_ARCH_arm64 +template <> +void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +{ + switch (kind) { + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + _adrpLocations.push_back(address); + break; + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreARM64PCRelToGOT: + _32bitPointerLocations.push_back(address); + break; + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + _64bitPointerLocations.push_back(address); + break; + default: + warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address); + break; + } +} +#endif template void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& locations) const @@ -1201,6 +1241,14 @@ void SplitSegInfoAtom::encode() const this->_encodedData.append_byte(0); // terminator } + if ( _adrpLocations.size() != 0 ) { + this->_encodedData.append_byte(3); + //fprintf(stderr, "type 3:\n"); + std::sort(_adrpLocations.begin(), _adrpLocations.end()); + this->uleb128EncodeAddresses(_adrpLocations); + this->_encodedData.append_byte(0); // terminator + } + if ( _thumbLo16Locations.size() != 0 ) { this->_encodedData.append_byte(5); //fprintf(stderr, "type 5:\n"); diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp index 60a4fa9..b9b1215 100644 --- a/src/ld/LinkEditClassic.hpp +++ b/src/ld/LinkEditClassic.hpp @@ -927,6 +927,9 @@ uint64_t ExternalRelocationsAtom::size() const return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info

); } +#if SUPPORT_ARCH_arm64 +template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM64_RELOC_UNSIGNED; } +#endif #if SUPPORT_ARCH_arm_any template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return ARM_RELOC_VANILLA; } #endif @@ -936,6 +939,10 @@ template <> uint32_t ExternalRelocationsAtom::pointerReloc() { return X8 template <> uint32_t ExternalRelocationsAtom::callReloc() { return X86_64_RELOC_BRANCH; } template <> uint32_t ExternalRelocationsAtom::callReloc() { return GENERIC_RELOC_VANILLA; } +#if SUPPORT_ARCH_arm64 +template <> uint32_t ExternalRelocationsAtom::callReloc() { return ARM64_RELOC_BRANCH26; } +#endif + template uint32_t ExternalRelocationsAtom::callReloc() { @@ -1647,6 +1654,205 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* } #endif +#if SUPPORT_ARCH_arm64 +template <> +void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* sect, + const Entry& entry, std::vector >& relocs) +{ + macho_relocation_info

reloc1; + macho_relocation_info

reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreARM64Branch26: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_BRANCH26); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64Page21: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64Page21: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_PAGE21); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64PageOff12: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_PAGEOFF12); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGE21); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGEOFF12); + relocs.push_back(reloc1); + break; + + + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreARM64PointerToGOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64PCRelToGOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT); + relocs.push_back(reloc1); + break; + + default: + assert(0 && "need to handle arm64 -r reloc"); + + } + +} +#endif // SUPPORT_ARCH_arm64 template diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 0131911..d916c53 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -104,19 +104,24 @@ void throwf(const char* format, ...) throw t; } -bool Options::FileInfo::checkFileExists(const char *p) + +bool Options::FileInfo::checkFileExists(const Options& options, const char *p) { struct stat statBuffer; - if (p == NULL) p = path; + if (p == NULL) + p = path; if ( stat(p, &statBuffer) == 0 ) { if (p != path) path = strdup(p); fileLen = statBuffer.st_size; modTime = statBuffer.st_mtime; return true; } + if ( options.dumpDependencyInfo() ) + options.dumpDependency(Options::depNotFound, p); return false; } + Options::Options(int argc, const char* argv[]) : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), @@ -131,7 +136,7 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), + fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), @@ -167,9 +172,14 @@ Options::Options(int argc, const char* argv[]) fSourceVersionLoadCommand(false), fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), fDependentDRInfo(false), fDependentDRInfoForcedOn(false), fDependentDRInfoForcedOff(false), + fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false), + fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), + fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), + fGenerateDtraceDOF(true), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), - fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL) + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -177,10 +187,18 @@ Options::Options(int argc, const char* argv[]) this->parsePostCommandLineEnvironmentSettings(); this->reconfigureDefaults(); this->checkIllegalOptionCombinations(); + + if ( this->dumpDependencyInfo() ) { + this->dumpDependency(depOutputFile, fOutputFile); + if ( fMapPath != NULL ) + this->dumpDependency(depOutputFile, fMapPath); + } } Options::~Options() { + if ( fDependencyFileDescriptor != -1 ) + ::close(fDependencyFileDescriptor); } bool Options::errorBecauseOfWarnings() const @@ -246,7 +264,11 @@ bool Options::allGlobalsAreDeadStripRoots() const // switch ( fOutputKind ) { case Options::kDynamicExecutable: + // Add the -export_dynamic flag + return fExportDynamic; case Options::kStaticExecutable: + // Support the -export_dynamic flag for xnu + return fExportDynamic; case Options::kPreload: // by default unused globals in a main executable are stripped return false; @@ -276,7 +298,6 @@ const char* Options::executablePath() return fExecutablePath; } - uint32_t Options::initialSegProtection(const char* segName) const { for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -478,6 +499,11 @@ bool Options::forceNotWeakNonWildcard(const char* symbolName) const return fForceNotWeakSymbols.containsNonWildcard(symbolName); } +bool Options::forceCoalesce(const char* symbolName) const +{ + return fForceCoalesceSymbols.contains(symbolName); +} + bool Options::shouldExport(const char* symbolName) const { @@ -533,27 +559,29 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fMacVersionMin = ld::mac10_6; #endif } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; break; case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); - #elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else - warning("-macosx_version_min not specified, assuming 10.6"); - fMacVersionMin = ld::mac10_6; + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); #endif } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; break; } fLinkSnapshot.recordArch(fArchitectureName); + // only use compressed LINKEDIT for: + // Mac OS X 10.6 or later + // iOS 3.1 or later + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB + if ( minOS(ld::mac10_5, ld::iOS_2_0) ) + fUseSimplifiedDylibReExports = true; return; } } @@ -581,20 +609,20 @@ bool Options::checkForFile(const char* format, const char* dir, const char* root { char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; sprintf(possiblePath, format, dir, rootName); - bool found = result.checkFileExists(possiblePath); + bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); return found; } -Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) +Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) const { FileInfo result; const int rootNameLen = strlen(rootName); // if rootName ends in .o there is no .a vs .dylib choice if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -608,14 +636,14 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) case kSearchAllDirsForDylibsThenAllDirsForArchives: // first look in all directories for just for dylibs if ( lookForDylibs ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; } - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -625,7 +653,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) } // next look in all directories for just for archives if ( !dylibsOnly ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -637,7 +665,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) case kSearchDylibAndArchiveInEachDir: // look in each directory for just for a dylib then for an archive - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -654,7 +682,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) throwf("library not found for -l%s", rootName); } -Options::FileInfo Options::findFramework(const char* frameworkName) +Options::FileInfo Options::findFramework(const char* frameworkName) const { if ( frameworkName == NULL ) throw "-framework missing next argument"; @@ -670,9 +698,9 @@ Options::FileInfo Options::findFramework(const char* frameworkName) return findFramework(name, suffix); } -Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const { - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { // ??? Shouldn't we be using String here and just initializing it? @@ -693,7 +721,7 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi } } FileInfo result; - bool found = result.checkFileExists(possiblePath); + bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound framework: '%s'\n", (found ? " " : " not "), possiblePath); @@ -724,13 +752,13 @@ Options::FileInfo Options::findFile(const char* path) const if ( possiblePath[sdkPathDirLen-1] == '/' ) possiblePath[sdkPathDirLen-1] = '\0'; strcat(possiblePath, path); - if ( result.checkFileExists(possiblePath) ) { + if ( result.checkFileExists(*this, possiblePath) ) { return result; } } } // try raw path - if ( result.checkFileExists(path) ) { + if ( result.checkFileExists(*this, path) ) { return result; } @@ -743,7 +771,7 @@ Options::FileInfo Options::findFile(const char* path) const strcpy(&addPoint[1], &path[17]); else strcpy(newPath, &path[17]); - if ( result.checkFileExists(newPath) ) { + if ( result.checkFileExists(*this, newPath) ) { return result; } } @@ -815,7 +843,8 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) const // If we didn't find it fall back to findFile. return findFile(path); } - + + void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) { @@ -886,12 +915,16 @@ void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdina file = fopen(realFileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, realFileOfPaths); } } else { file = fopen(fileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, fileOfPaths); } char path[PATH_MAX]; @@ -1064,6 +1097,9 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, SetW if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read %s file: %s", option, fileOfExports); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfExports); + ::close(fd); // parse into symbols and add to unordered_set @@ -1135,6 +1171,8 @@ void Options::parseAliasFile(const char* fileOfAliases) throwf("can't read alias file: %s", fileOfAliases); p[stat_buf.st_size] = '\n'; ::close(fd); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfAliases); // parse into symbols and add to fAliases AliasPair pair; @@ -1248,7 +1286,14 @@ void Options::setMacOSXVersionMin(const char* version) throw "-macosx_version_min argument missing"; if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - unsigned int minorVersion = version[3] - '0'; + unsigned int minorVersion = 0; + for (int i=3; isdigit(version[i]); ++i) { + minorVersion = minorVersion*10 + (version[i] - '0'); + } + if ( minorVersion > 255 ) { + warning("Mac OS X minor version > 255 in '%s'", version); + minorVersion = 255; + } fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); } else { @@ -1533,6 +1578,8 @@ void Options::parseOrderFile(const char* path, bool cstring) throwf("can't read order file: %s", path); ::close(fd); p[stat_buf.st_size] = '\n'; + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, path); // parse into vector of pairs char * const end = &p[stat_buf.st_size+1]; @@ -1592,6 +1639,14 @@ void Options::parseOrderFile(const char* path, bool cstring) objFileName = symbolStart; symbolStart = &colon[3]; } + else { + colon = strstr(symbolStart, ".o):"); + if ( colon != NULL ) { + colon[3] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[4]; + } + } // trim leading spaces while ( isspace(*symbolStart) ) ++symbolStart; @@ -2264,13 +2319,34 @@ void Options::parse(int argc, const char* argv[]) } // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { - setMacOSXVersionMin(argv[++i]); + const char* macVers = argv[++i]; + const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { + // when conflicting deployments set, break tie by looking at syslibroot + warning("both MACOSX_DEPLOYMENT_TARGET and IPHONEOS_DEPLOYMENT_TARGET are set"); + if ( !fSDKPaths.empty() ) { + const char* sysrootPath = fSDKPaths.back(); + const char* lastSlash = strrchr(sysrootPath, '/'); + if ( strstr(lastSlash, "Simulator") != NULL ) + setIOSVersionMin(enviPhoneVers); + else + setMacOSXVersionMin(macVers); + } + else { + setMacOSXVersionMin(macVers); + } + } + else { + setMacOSXVersionMin(macVers); + } } else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { setIOSVersionMin(argv[++i]); } else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) { setIOSVersionMin(argv[++i]); + fTargetIOSSimulator = true; } else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); @@ -2603,6 +2679,12 @@ void Options::parse(int argc, const char* argv[]) throw "missing argument to -mllvm"; fLLVMOptions.push_back(opts); } + else if ( strcmp(arg, "-mcpu") == 0 ) { + const char* cpu = argv[++i]; + if ( cpu == NULL ) + throw "missing argument to -mcpu"; + fLtoCpu = cpu; + } else if ( strcmp(arg, "-no_order_inits") == 0 ) { fAutoOrderInitializers = false; } @@ -2755,6 +2837,14 @@ void Options::parse(int argc, const char* argv[]) else if (strcmp(arg, "-debug_snapshot") == 0) { fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); fSnapshotRequested = true; + } + else if (strcmp(arg, "-snapshot_dir") == 0) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-snapshot_dir missing path"; + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fLinkSnapshot.setSnapshotPath(path); + fSnapshotRequested = true; } else if ( strcmp(arg, "-new_main") == 0 ) { fEntryPointLoadCommandForceOn = true; @@ -2789,6 +2879,53 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { fKextsUseStubs = true; } + else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-export_dynamic") == 0 ) { + fExportDynamic = true; + } + else if ( strcmp(arg, "-force_symbols_coalesce_list") == 0 ) { + snapshotFileArgIndex = 1; + loadExportFile(argv[++i], "-force_symbols_coalesce_list", fForceCoalesceSymbols); + } + else if ( strcmp(arg, "-add_linker_option") == 0 ) { + // ex: -add_linker_option '-framework Foundation' + const char* optString = argv[++i]; + if ( optString == NULL ) + throw "-add_linker_option missing ::Entry* File::Entry::next() const template <> cpu_type_t File::architecture() { return CPU_TYPE_I386; } template <> cpu_type_t File::architecture() { return CPU_TYPE_X86_64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM64; } template @@ -323,13 +324,13 @@ bool File::memberHasObjCCategories(const Entry* member) const template typename File::MemberState& File::makeObjectFileForMember(const Entry* member) const { - uint16_t memberIndex = 0; + uint32_t memberIndex = 0; // in case member was instantiated earlier but not needed yet typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member); if ( pos == _instantiatedEntries.end() ) { // Have to find the index of this member const Entry* start; - uint16_t index; + uint32_t index; if (_instantiatedEntries.size() == 0) { start = (Entry*)&_archiveFileContent[8]; index = 1; @@ -598,6 +599,12 @@ ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; #endif } return NULL; diff --git a/src/ld/parsers/libunwind/DwarfInstructions.hpp b/src/ld/parsers/libunwind/DwarfInstructions.hpp index b04582d..c67a237 100644 --- a/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -95,7 +95,8 @@ public: typedef typename A::sint_t sint_t; static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, - CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn); + const pint_t cuStarts[], uint32_t cuCount, bool keepDwarfWhichHasCU, bool forceDwarfConversion, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn); static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, @@ -152,6 +153,18 @@ private: static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, const Registers_ppc&, const typename CFI_Parser::PrologInfo& prolog, char warningBuffer[1024]); + + // arm64 specific variants + static bool isReturnAddressRegister(int regNum, const Registers_arm64&); + static int lastRestoreReg(const Registers_arm64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_arm64&); + static bool checkRegisterPair(uint32_t reg, const typename CFI_Parser::PrologInfo& prolog, + int& offset, char warningBuffer[1024]); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_arm64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm64&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + }; @@ -159,7 +172,8 @@ private: template const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, - CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn) + const pint_t cuStarts[], uint32_t cuCount, bool keepDwarfWhichHasCU, bool forceDwarfConversion, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn) { typename CFI_Parser::CIE_Info cieInfo; CFI_Atom_Info* entry = infos; @@ -221,7 +235,6 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); - // test if pc is within the function this FDE covers entry->u.fdeInfo.function.targetAddress = pcStart; entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress; entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding; @@ -243,34 +256,60 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS } p = endOfAug; } - // compute compact unwind encoding - typename CFI_Parser::FDE_Info fdeInfo; - fdeInfo.fdeStart = currentCFI; - fdeInfo.fdeLength = nextCFI - currentCFI; - fdeInfo.fdeInstructions = p; - fdeInfo.pcStart = pcStart; - fdeInfo.pcEnd = pcStart + pcRange; - fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; - typename CFI_Parser::PrologInfo prolog; - R dummy; // for proper selection of architecture specific functions - if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { - char warningBuffer[1024]; - entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); - if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) - entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; - if ( warningBuffer[0] != '\0' ) - warn(ref, fdeInfo.pcStart, warningBuffer); + // See if already is a compact unwind for this address. + bool alreadyHaveCU = false; + for (uint32_t i=0; i < cuCount; ++i) { + if (cuStarts[i] == entry->u.fdeInfo.function.targetAddress) { + alreadyHaveCU = true; + break; + } + } + //fprintf(stderr, "FDE for func at 0x%08X, alreadyHaveCU=%d\n", (uint32_t)entry->u.fdeInfo.function.targetAddress, alreadyHaveCU); + if ( alreadyHaveCU && !forceDwarfConversion ) { + if ( keepDwarfWhichHasCU ) + ++entry; } else { - warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); - entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + if ( (cuCount != 0) && !forceDwarfConversion ) { + // Have some compact unwind, so this is a new .o file, therefore anything without + // compact unwind must be something not expressable in compact unwind. + R dummy; + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + else { + // compute compact unwind encoding by parsing dwarf + typename CFI_Parser::FDE_Info fdeInfo; + fdeInfo.fdeStart = currentCFI; + fdeInfo.fdeLength = nextCFI - currentCFI; + fdeInfo.fdeInstructions = p; + fdeInfo.pcStart = pcStart; + fdeInfo.pcEnd = pcStart + pcRange; + fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; + typename CFI_Parser::PrologInfo prolog; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + char warningBuffer[1024]; + entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) + entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; + if ( warningBuffer[0] != '\0' ) + warn(ref, fdeInfo.pcStart, warningBuffer); + } + else { + warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + } + ++entry; } - ++entry; } p = nextCFI; } - if ( entry != end ) - return "wrong entry count for parseCFIs"; + if ( entry != end ) { + //fprintf(stderr, "DwarfInstructions::parseCFIs() infosCount was %d on input, now %ld\n", infosCount, entry - infos); + infosCount = (entry - infos); + } + return NULL; // success } @@ -1037,7 +1076,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo for (int i=0; i < 64; ++i) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { - sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_64_MODE_DWARF; } switch (i) { @@ -1184,15 +1223,19 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); return UNWIND_X86_64_MODE_DWARF; } - pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + #if __EXCEPTIONS try { + #endif uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + #if __EXCEPTIONS } catch (...) { strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); return UNWIND_X86_64_MODE_DWARF; } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { @@ -1410,7 +1453,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo for (int i=0; i < 64; ++i) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { - sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_MODE_DWARF; } switch (i) { @@ -1554,12 +1597,22 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo if ( stackValue > stackMaxImmedValue ) { // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; - uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); - stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + #if __EXCEPTIONS + try { + #endif + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + #if __EXCEPTIONS + } + catch (...) { + strcpy(warningBuffer, "stack size is large but stack subl instruction not found"); + return UNWIND_X86_MODE_DWARF; + } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { - strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + strcpy(warningBuffer, "stack subl instruction is too different from dwarf stack size"); return UNWIND_X86_MODE_DWARF; } encoding = UNWIND_X86_MODE_STACK_IND; @@ -1715,6 +1768,195 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo +// +// arm64 specific functions +// + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_arm64&) +{ + return UNWIND_ARM64_MODE_DWARF; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_arm64&) +{ + return (regNum == UNW_ARM64_LR); +} + +template +int DwarfInstructions::lastRestoreReg(const Registers_arm64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_ARM64_D31 ); + return UNW_ARM64_D31; +} + + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_arm64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else + ABORT("getCFA(): unsupported location for arm64 cfa"); +} + +template +bool DwarfInstructions::checkRegisterPair(uint32_t reg, const typename CFI_Parser::PrologInfo& prolog, + int& offset, char warningBuffer[1024]) +{ + if ( (prolog.savedRegisters[reg].location != CFI_Parser::kRegisterUnused) + || (prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterUnused) ) { + if ( prolog.savedRegisters[reg].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg); + return false; + } + if ( prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != prolog.savedRegisters[reg+1].value + 8 ) { + sprintf(warningBuffer, "registers %d and %d not saved contiguously in frame", reg, reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != offset ) { + sprintf(warningBuffer, "registers %d not saved contiguously in frame", reg); + return false; + } + offset -= 16; + return true; + } + return false; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == UNW_ARM64_LR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_ARM64_MODE_DWARF; + } + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_ARM64_MODE_DWARF; + } + + compact_unwind_encoding_t encoding = 0; + int offset = 0; + + // figure out which kind of frame this function uses + bool standardFPframe = ( + (prolog.cfaRegister == UNW_ARM64_FP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_ARM64_FP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_FP].value == -16) + && (prolog.savedRegisters[UNW_ARM64_LR].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_LR].value == -8) ); + + bool standardFrameless = ( prolog.cfaRegister == UNW_ARM64_SP ); + + if ( standardFrameless ) { + // verify enough space for registers saved + int count = 0; + for (int i=0; i < 96; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) + ++count; + } + if ( count * 8 > prolog.cfaRegisterOffset ) { + strcpy(warningBuffer, "saved registers do not fit in stack size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( (prolog.cfaRegisterOffset % 16) != 0 ) { + strcpy(warningBuffer, "stack size is not 16-byte multiple"); + return UNWIND_ARM64_MODE_DWARF; + } + const int32_t maxStack = (UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK >> __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + if ( (prolog.cfaRegisterOffset / 16) > maxStack ) { + strcpy(warningBuffer, "stack size is too large for frameless function"); + return UNWIND_ARM64_MODE_DWARF; + } + encoding = UNWIND_ARM64_MODE_FRAMELESS | ((prolog.cfaRegisterOffset/16) << __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + offset = -16; + } + else if ( standardFPframe ) { + encoding = UNWIND_ARM64_MODE_FRAME; + offset = -24; + } + else { + // no compact encoding for this + strcpy(warningBuffer, "does not use standard frame"); + return UNWIND_ARM64_MODE_DWARF; + } + + // make sure no volatile registers are saved + for (int i=UNW_ARM64_X0; i < UNW_ARM64_X19; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_SP+1; i < UNW_ARM64_D8; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_D16; i < UNW_ARM64_D31+1; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + + // compute encoding + bool X19_X20_saved = checkRegisterPair(UNW_ARM64_X19, prolog, offset, warningBuffer); + bool X21_X22_saved = checkRegisterPair(UNW_ARM64_X21, prolog, offset, warningBuffer); + bool X23_X24_saved = checkRegisterPair(UNW_ARM64_X23, prolog, offset, warningBuffer); + bool X25_X26_saved = checkRegisterPair(UNW_ARM64_X25, prolog, offset, warningBuffer); + bool X27_X28_saved = checkRegisterPair(UNW_ARM64_X27, prolog, offset, warningBuffer); + bool D8_D9_saved = checkRegisterPair(UNW_ARM64_D8, prolog, offset, warningBuffer); + bool D10_D11_saved = checkRegisterPair(UNW_ARM64_D10, prolog, offset, warningBuffer); + bool D12_D13_saved = checkRegisterPair(UNW_ARM64_D12, prolog, offset, warningBuffer); + bool D14_D15_saved = checkRegisterPair(UNW_ARM64_D14, prolog, offset, warningBuffer); + if ( warningBuffer[0] != '\0' ) + return UNWIND_ARM64_MODE_DWARF; + + if ( X19_X20_saved ) + encoding |= UNWIND_ARM64_FRAME_X19_X20_PAIR; + if ( X21_X22_saved ) + encoding |= UNWIND_ARM64_FRAME_X21_X22_PAIR; + if ( X23_X24_saved ) + encoding |= UNWIND_ARM64_FRAME_X23_X24_PAIR; + if ( X25_X26_saved ) + encoding |= UNWIND_ARM64_FRAME_X25_X26_PAIR; + if ( X27_X28_saved ) + encoding |= UNWIND_ARM64_FRAME_X27_X28_PAIR; + if ( D8_D9_saved ) + encoding |= UNWIND_ARM64_FRAME_D8_D9_PAIR; + if ( D10_D11_saved ) + encoding |= UNWIND_ARM64_FRAME_D10_D11_PAIR; + if ( D12_D13_saved ) + encoding |= UNWIND_ARM64_FRAME_D12_D13_PAIR; + if ( D14_D15_saved ) + encoding |= UNWIND_ARM64_FRAME_D14_D15_PAIR; + + return encoding; +} } // namespace libunwind diff --git a/src/ld/parsers/libunwind/DwarfParser.hpp b/src/ld/parsers/libunwind/DwarfParser.hpp index 3824d2e..2e4a3bb 100644 --- a/src/ld/parsers/libunwind/DwarfParser.hpp +++ b/src/ld/parsers/libunwind/DwarfParser.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * diff --git a/src/ld/parsers/libunwind/Registers.hpp b/src/ld/parsers/libunwind/Registers.hpp index 7d39fd7..0247066 100644 --- a/src/ld/parsers/libunwind/Registers.hpp +++ b/src/ld/parsers/libunwind/Registers.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2007-2009 Apple Inc. All rights reserved. + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -1035,9 +1035,288 @@ inline const char* Registers_ppc::getRegisterName(int regNum) return "unknown register"; } +} + + + + + +struct arm_thread_state64_t +{ + __uint64_t __x[29]; /* General purpose registers x0-x28 */ + __uint64_t __fp; /* Frame pointer x29 */ + __uint64_t __lr; /* Link register x30 */ + __uint64_t __sp; /* Stack pointer x31 */ + __uint64_t __pc; /* Program counter */ + __uint32_t __cpsr; /* Current program status register */ + __uint32_t padding[3]; /* round up struct size to be 0x110 */ +}; + + +/// +/// Registers_arm64 holds the register state of a thread in a 64-bit intel process. +/// +class Registers_arm64 +{ +public: + Registers_arm64(); + Registers_arm64(const void* registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto(); + uint64_t getSP() const { return fRegisters.__sp; } + void setSP(uint64_t value) { fRegisters.__sp = value; } + uint64_t getIP() const { return fRegisters.__pc; } + void setIP(uint64_t value) { fRegisters.__pc = value; } + uint64_t getFP() const { return fRegisters.__fp; } + void setFP(uint64_t value) { fRegisters.__fp = value; } +private: + arm_thread_state64_t fRegisters; + double fHalfVectorRegisters[32]; + // Currently only the lower double in 128-bit vectore registers + // is perserved during unwinding. We could define new register + // numbers (> 96) which mean whole vector registers, then this + // struct would need to change to contain whole vector registers. +}; + +inline Registers_arm64::Registers_arm64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_arm64) < sizeof(unw_context_t) ); + fRegisters = *((arm_thread_state64_t*)registers); +} + +inline Registers_arm64::Registers_arm64() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fRegisters, sizeof(fHalfVectorRegisters)); +} + + +inline bool Registers_arm64::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 95 ) + return false; + if ( (regNum > 31) && (regNum < 64) ) + return false; + return true; +} + +inline uint64_t Registers_arm64::getRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return fRegisters.__pc; + if ( regNum == UNW_REG_SP ) + return fRegisters.__sp; + if ( (regNum >= 0) && (regNum < 32) ) + return fRegisters.__x[regNum]; + ABORT("unsupported arm64 register"); +} + +inline void Registers_arm64::setRegister(int regNum, uint64_t value) +{ + if ( regNum == UNW_REG_IP ) + fRegisters.__pc = value; + else if ( regNum == UNW_REG_SP ) + fRegisters.__sp = value; + else if ( (regNum >= 0) && (regNum < 32) ) + fRegisters.__x[regNum] = value; + else + ABORT("unsupported arm64 register"); +} + +inline const char* Registers_arm64::getRegisterName(int regNum) +{ + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_ARM64_X0: + return "x0"; + case UNW_ARM64_X1: + return "x1"; + case UNW_ARM64_X2: + return "x2"; + case UNW_ARM64_X3: + return "x3"; + case UNW_ARM64_X4: + return "x4"; + case UNW_ARM64_X5: + return "x5"; + case UNW_ARM64_X6: + return "x6"; + case UNW_ARM64_X7: + return "x7"; + case UNW_ARM64_X8: + return "x8"; + case UNW_ARM64_X9: + return "x9"; + case UNW_ARM64_X10: + return "x10"; + case UNW_ARM64_X11: + return "x11"; + case UNW_ARM64_X12: + return "x12"; + case UNW_ARM64_X13: + return "x13"; + case UNW_ARM64_X14: + return "x14"; + case UNW_ARM64_X15: + return "x15"; + case UNW_ARM64_X16: + return "x16"; + case UNW_ARM64_X17: + return "x17"; + case UNW_ARM64_X18: + return "x18"; + case UNW_ARM64_X19: + return "x19"; + case UNW_ARM64_X20: + return "x20"; + case UNW_ARM64_X21: + return "x21"; + case UNW_ARM64_X22: + return "x22"; + case UNW_ARM64_X23: + return "x23"; + case UNW_ARM64_X24: + return "x24"; + case UNW_ARM64_X25: + return "x25"; + case UNW_ARM64_X26: + return "x26"; + case UNW_ARM64_X27: + return "x27"; + case UNW_ARM64_X28: + return "x28"; + case UNW_ARM64_X29: + return "fp"; + case UNW_ARM64_X30: + return "lr"; + case UNW_ARM64_X31: + return "sp"; + case UNW_ARM64_D0: + return "d0"; + case UNW_ARM64_D1: + return "d1"; + case UNW_ARM64_D2: + return "d2"; + case UNW_ARM64_D3: + return "d3"; + case UNW_ARM64_D4: + return "d4"; + case UNW_ARM64_D5: + return "d5"; + case UNW_ARM64_D6: + return "d6"; + case UNW_ARM64_D7: + return "d7"; + case UNW_ARM64_D8: + return "d8"; + case UNW_ARM64_D9: + return "d9"; + case UNW_ARM64_D10: + return "d10"; + case UNW_ARM64_D11: + return "d11"; + case UNW_ARM64_D12: + return "d12"; + case UNW_ARM64_D13: + return "d13"; + case UNW_ARM64_D14: + return "d14"; + case UNW_ARM64_D15: + return "d15"; + case UNW_ARM64_D16: + return "d16"; + case UNW_ARM64_D17: + return "d17"; + case UNW_ARM64_D18: + return "d18"; + case UNW_ARM64_D19: + return "d19"; + case UNW_ARM64_D20: + return "d20"; + case UNW_ARM64_D21: + return "d21"; + case UNW_ARM64_D22: + return "d22"; + case UNW_ARM64_D23: + return "d23"; + case UNW_ARM64_D24: + return "d24"; + case UNW_ARM64_D25: + return "d25"; + case UNW_ARM64_D26: + return "d26"; + case UNW_ARM64_D27: + return "d27"; + case UNW_ARM64_D28: + return "d28"; + case UNW_ARM64_D29: + return "d29"; + case UNW_ARM64_D30: + return "d30"; + case UNW_ARM64_D31: + return "d31"; + default: + return "unknown register"; + } +} + +bool Registers_arm64::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_ARM64_D0 ) + return false; + if ( regNum > UNW_ARM64_D31 ) + return false; + return true; +} + +double Registers_arm64::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fHalfVectorRegisters[regNum-UNW_ARM64_D0]; +} + +void Registers_arm64::setFloatRegister(int regNum, double value) +{ + assert(validFloatRegister(regNum)); + fHalfVectorRegisters[regNum-UNW_ARM64_D0] = value; } +inline bool Registers_arm64::validVectorRegister(int regNum) const +{ + return false; +} + +inline v128 Registers_arm64::getVectorRegister(int regNum) const +{ + ABORT("no arm64 vector register support yet"); +} + +inline void Registers_arm64::setVectorRegister(int regNum, v128 value) +{ + ABORT("no arm64 vector register support yet"); +} + + } // namespace libunwind diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 023f6e3..460dcf0 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -106,6 +106,8 @@ public: { return _debugInfoModTime; } virtual const std::vector* stabs() const { return NULL; } virtual bool canScatterAtoms() const { return true; } + virtual LinkerOptionsList* linkerOptions() const { return NULL; } + lto_module_t module() { return _module; } class InternalAtom& internalAtom() { return _internalAtom; } @@ -290,7 +292,9 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.architecture = options.arch; objOpts.objSubtypeMustMatch = false; objOpts.logAllFiles = false; - objOpts.convertUnwindInfo = true; + objOpts.warnUnwindConversionProblems = options.needsUnwindInfoSection; + objOpts.keepDwarfUnwind = options.keepDwarfUnwind; + objOpts.forceDwarfConversion = false; objOpts.subType = 0; // mach-o parsing is done in-memory, but need path for debug notes @@ -447,6 +451,16 @@ void Atom::setCompiledAtom(const ld::Atom& atom) +// The order that files are merged must match command line order +struct CommandLineOrderFileSorter +{ + bool operator()(File* left, File* right) + { + return ( left->ordinal() < right->ordinal() ); + } +}; + + bool Parser::optimize( const std::vector& allAtoms, ld::Internal& state, const OptimizeOptions& options, @@ -469,10 +483,16 @@ bool Parser::optimize( const std::vector& allAtoms, // create optimizer and add each Reader lto_code_gen_t generator = ::lto_codegen_create(); + // The order that files are merged must match command line order + std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); + ld::File::Ordinal lastOrdinal; for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", (*it)->path()); - if ( ::lto_codegen_add_module(generator, (*it)->module()) ) - throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", (*it)->path(), ::lto_get_error_message(), ::lto_get_version()); + File* f = *it; + assert(f->ordinal() > lastOrdinal); + if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); + if ( ::lto_codegen_add_module(generator, f->module()) ) + throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); + lastOrdinal = f->ordinal(); } // add any -mllvm command line options @@ -481,6 +501,10 @@ bool Parser::optimize( const std::vector& allAtoms, ::lto_codegen_debug_options(generator, *it); } + // Need a way for LTO to get cpu variants (until that info is in bitcode) + if ( options.mcpu != NULL ) + ::lto_codegen_set_cpu(generator, options.mcpu); + // The atom graph uses directed edges (references). Collect all references where // originating atom is not part of any LTO Reader. This allows optimizer to optimize an // external (i.e. not originated from same .o file) reference if all originating atoms are also @@ -775,7 +799,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) fit->u.target = pos->second; } else { - if ( _deadllvmAtoms.find(targetName) != _deadllvmAtoms.end() ) { + // Don't unbind follow-on reference into by-name reference + if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) { // target was coalesed away and replace by mach-o atom from a non llvm .o file fit->binding = ld::Fixup::bindingByNameUnbound; fit->u.name = targetName; diff --git a/src/ld/parsers/lto_file.h b/src/ld/parsers/lto_file.h index 3503fa9..4140a3a 100644 --- a/src/ld/parsers/lto_file.h +++ b/src/ld/parsers/lto_file.h @@ -53,7 +53,10 @@ struct OptimizeOptions { bool relocatable; bool allowTextRelocs; bool linkerDeadStripping; + bool needsUnwindInfoSection; + bool keepDwarfUnwind; cpu_type_t arch; + const char* mcpu; const std::vector* llvmOptions; }; diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp index aad1a8b..b30e727 100644 --- a/src/ld/parsers/macho_dylib_file.cpp +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -1,3 +1,4 @@ + /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2005-2011 Apple Inc. All rights reserved. @@ -142,7 +143,7 @@ public: File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool addVers, + ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool allowSimToMacOSX, bool addVers, bool logAllFiles, const char* installPath, bool indirectDylib); virtual ~File() {} @@ -162,6 +163,7 @@ public: virtual bool hasWeakDefinition(const char* name) const; virtual bool allSymbolsAreWeakImported() const; virtual const void* codeSignatureDR() const { return _codeSignatureDR; } + virtual bool installPathVersionSpecific() const { return _installPathOverride; } protected: @@ -194,6 +196,7 @@ private: bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const; bool isPublicLocation(const char* pth); + bool wrongOS() { return _wrongOS; } void addSymbol(const char* name, bool weak, bool tlv, pint_t address); void addDyldFastStub(); void buildExportHashTableFromExportInfo(const macho_dyld_info_command

* dyldInfo, @@ -206,6 +209,7 @@ private: const ld::MacVersionMin _macVersionMin; const ld::IOSVersionMin _iOSVersionMin; + const bool _allowSimToMacOSXLinking; const bool _addVersionLoadCommand; bool _linkingFlat; bool _implicitlyLinkPublicDylibs; @@ -225,7 +229,9 @@ private: bool _hasPublicInstallName; mutable bool _providedAtom; bool _explictReExportFound; - + bool _wrongOS; + bool _installPathOverride; + static bool _s_logHashtable; }; @@ -243,10 +249,10 @@ template const char* File::objCInfoSectionName() { return "__ima template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool addVers, + ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool allowSimToMacOSX, bool addVers, bool logAllFiles, const char* targetInstallPath, bool indirectDylib) : ld::dylib::File(strdup(pth), mTime, ord), - _macVersionMin(macMin), _iOSVersionMin(iOSMin), _addVersionLoadCommand(addVers), + _macVersionMin(macMin), _iOSVersionMin(iOSMin), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), _objcContraint(ld::File::objcConstraintNone), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), @@ -254,7 +260,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL), _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), - _providedAtom(false), _explictReExportFound(false) + _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -331,12 +337,18 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); break; case LC_VERSION_MIN_MACOSX: - if ( _addVersionLoadCommand && !indirectDylib && (_iOSVersionMin != ld::iOSVersionUnset) ) - warning("building for iOS, but linking against dylib built for MacOSX: %s", pth); + if ( (_iOSVersionMin != ld::iOSVersionUnset) && !_allowSimToMacOSXLinking ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib ) + throw "building for iOS Simulator, but linking against dylib built for MacOSX"; + } break; case LC_VERSION_MIN_IPHONEOS: - if ( _addVersionLoadCommand && !indirectDylib && (_macVersionMin != ld::macVersionUnset) ) - warning("building for MacOSX, but linking against dylib built for iOS: %s", pth); + if ( _macVersionMin != ld::macVersionUnset ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib ) + throw "building for MacOSX, but linking against dylib built for iOS Simulator"; + } break; case LC_CODE_SIGNATURE: codeSignature = (macho_linkedit_data_command

* )cmd; @@ -355,6 +367,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // }; // #define OBJC_IMAGE_SUPPORTS_GC 2 // #define OBJC_IMAGE_GC_ONLY 4 + // #define OBJC_IMAGE_IS_SIMULATED 32 // const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); if ( (sect->size() >= 8) && (contents[0] == 0) ) { @@ -363,6 +376,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _objcContraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( (flags & 32) == 32 ) + _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator; else _objcContraint = ld::File::objcConstraintRetainRelease; } @@ -605,6 +620,7 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address } else if ( strncmp(symAction, "install_name$", 13) == 0 ) { _dylibInstallPath = symName; + _installPathOverride = true; return; } else { @@ -794,7 +810,7 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child it->dylib = (File*)handler->findDylib(it->path, this->path()); - if ( it->dylib->hasPublicInstallName() ) { + if ( it->dylib->hasPublicInstallName() && !it->dylib->wrongOS() ) { // promote this child to be automatically added as a direct dependent if this already is if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); @@ -866,6 +882,7 @@ public: typedef typename A::P P; static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle); + static const char* fileKind(const uint8_t* fileContent); static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { @@ -875,6 +892,7 @@ public: opts.implicitlyLinkIndirectPublicDylibs(), opts.macosxVersionMin(), opts.iOSVersionMin(), + opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), opts.logAllFiles(), opts.installPath(), @@ -968,6 +986,145 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + switch ( header->filetype() ) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return true; + case MH_BUNDLE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB)"; + case MH_EXECUTE: + if ( executableOrDyliborBundle ) + return true; + else + throw "can't link with a main executable"; + default: + return false; + } +} + + +bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult) +{ + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_X86_64; + *subResult = CPU_SUBTYPE_X86_64_ALL; + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_I386; + *subResult = CPU_SUBTYPE_X86_ALL; + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_ARM; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_ARM64; + *subResult = CPU_SUBTYPE_ARM64_ALL; + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_POWERPC; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); + return true; + } + if ( Parser::validFile(fileContent, false) ) { + *result = CPU_TYPE_POWERPC64; + *subResult = CPU_SUBTYPE_POWERPC_ALL; + return true; + } + return false; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_I386 ) + return NULL; + return "i386"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return NULL; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return NULL; + return "x86_64"; +} + +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM ) + return NULL; + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (t->cpuType == CPU_TYPE_ARM) && ((cpu_subtype_t)header->cpusubtype() == t->cpuSubType) ) { + return t->archName; + } + } + return "arm???"; +} + +#if SUPPORT_ARCH_arm64 +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return NULL; + return "arm64"; +} +#endif + +// +// used by linker is error messages to describe mismatched files +// +const char* archName(const uint8_t* fileContent) +{ + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } +#if SUPPORT_ARCH_arm64 + if ( Parser::validFile(fileContent, false) ) { + return Parser::fileKind(fileContent); + } +#endif + return NULL; +} + + // // main function used by linker to instantiate ld::Files // @@ -993,6 +1150,12 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( Parser::validFile(fileContent, bundleLoader) ) return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; #endif } return NULL; diff --git a/src/ld/parsers/macho_dylib_file.h b/src/ld/parsers/macho_dylib_file.h index ad03af1..4d093f1 100644 --- a/src/ld/parsers/macho_dylib_file.h +++ b/src/ld/parsers/macho_dylib_file.h @@ -31,6 +31,12 @@ namespace mach_o { namespace dylib { + +extern bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult); + +extern const char* archName(const uint8_t* fileContent); + + extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, const Options& opts, ld::File::Ordinal ordinal, bool bundleLoader, bool indirectDylib); diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index b49d63f..c22af18 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -92,9 +92,10 @@ public: virtual ObjcConstraint objCConstraint() const { return _objConstraint; } virtual uint32_t cpuSubType() const { return _cpuSubType; } virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } - virtual const std::vector* stabs() const { return &_stabs; } + virtual const std::vector* stabs() const { return &_stabs; } virtual bool canScatterAtoms() const { return _canScatterAtoms; } virtual const char* translationUnitSource() const; + virtual LinkerOptionsList* linkerOptions() const { return &_linkerOptions; } const uint8_t* fileContent() { return _fileContent; } private: @@ -123,6 +124,7 @@ private: ld::File::ObjcConstraint _objConstraint; uint32_t _cpuSubType; bool _canScatterAtoms; + std::vector > _linkerOptions; }; @@ -154,6 +156,7 @@ public: virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const { return 0; } virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const { return false; } + virtual bool ignoreLabel(const char* label) const { return false; } static const char* makeSectionName(const macho_section* s); protected: @@ -235,7 +238,7 @@ public: typedef typename A::P::uint_t pint_t; typedef libunwind::CFI_Atom_Info CFI_Atom_Info; - void cfiParse(class Parser& parser, uint8_t* buffer, CFI_Atom_Info cfiArray[], uint32_t cfiCount); + void cfiParse(class Parser& parser, uint8_t* buffer, CFI_Atom_Info cfiArray[], uint32_t& cfiCount, const pint_t cuStarts[], uint32_t cuCount); bool needsRelocating(); static bool bigEndian(); @@ -274,6 +277,7 @@ public: uint32_t count(); void parse(class Parser& parser, uint32_t cnt, Info array[]); + static bool encodingMeansUseDwarf(compact_unwind_encoding_t enc); private: @@ -379,16 +383,17 @@ protected: virtual bool addFollowOnFixups() const { return false; } virtual const char* unlabeledAtomName(Parser& parser, pint_t addr) = 0; - virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableNotIn; } + virtual ld::Atom::SymbolTableInclusion symbolTableInclusion(); virtual pint_t elementSizeAtAddress(pint_t addr) = 0; virtual ld::Atom::Scope scopeAtAddress(Parser& parser, pint_t addr) { return ld::Atom::scopeLinkageUnit; } virtual bool useElementAt(Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, pint_t addr) = 0; virtual ld::Atom::Definition definition() { return ld::Atom::definitionRegular; } virtual ld::Atom::Combine combine(Parser& parser, pint_t addr) = 0; - virtual bool ignoreLabel(const char* label) { return (label[0] == 'L'); } + virtual bool ignoreLabel(const char* label) const { return (label[0] == 'L'); } }; + template class FixedSizeSection : public ImplicitSizeSection { @@ -481,7 +486,7 @@ protected: virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Scope scopeAtAddress(Parser& parser, pint_t addr); virtual ld::Atom::Combine combine(Parser&, pint_t); - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -505,7 +510,7 @@ protected: virtual const char* unlabeledAtomName(Parser&, pint_t) { return "CFString"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return 4*sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -533,7 +538,7 @@ protected: virtual ld::Atom::SymbolTableInclusion symbolTableInclusion() { return ld::Atom::symbolTableIn; } virtual pint_t elementSizeAtAddress(pint_t addr); virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineNever; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const { return 0; } virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, @@ -555,7 +560,7 @@ protected: virtual const char* unlabeledAtomName(Parser&, pint_t) { return "objc-class-ref"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -578,7 +583,7 @@ protected: virtual const char* unlabeledAtomName(Parser&, pint_t) { return "objc-cat-list"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineNever; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } private: const char* targetClassName(const class Atom* atom, const ld::IndirectBindingTable& ind) const; }; @@ -597,7 +602,7 @@ protected: virtual const char* unlabeledAtomName(Parser&, pint_t) { return "pointer-to-literal-cstring"; } virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndReferences; } - virtual bool ignoreLabel(const char* label) { return true; } + virtual bool ignoreLabel(const char* label) const { return true; } virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, const ld::IndirectBindingTable& ind) const; @@ -635,7 +640,7 @@ protected: virtual Atom* findAtomByAddress(pint_t addr); virtual const char* unlabeledAtomName(Parser&, pint_t) { return "cstring"; } virtual pint_t elementSizeAtAddress(pint_t addr); - virtual bool ignoreLabel(const char* label); + virtual bool ignoreLabel(const char* label) const; virtual bool useElementAt(Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, pint_t addr); virtual ld::Atom::Combine combine(Parser&, pint_t) { return ld::Atom::combineByNameAndContent; } @@ -865,7 +870,8 @@ public: const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) { Parser p(fileContent, fileLength, path, modTime, - ordinal, opts.convertUnwindInfo); + ordinal, opts.warnUnwindConversionProblems, + opts.keepDwarfUnwind, opts.forceDwarfConversion); return p.parse(opts); } @@ -934,7 +940,7 @@ public: _allFixups.push_back(FixupInAtom(src, c, k)); } - + const char* path() { return _path; } uint32_t symbolCount() { return _symbolCount; } uint32_t indirectSymbol(uint32_t indirectIndex); const macho_nlist

& symbolFromIndex(uint32_t index); @@ -971,9 +977,11 @@ public: unsigned int stubsSectionNum() { return _stubsSectionNum; } void addDtraceExtraInfos(const SourceLocation& src, const char* provider); const char* scanSymbolTableForAddress(uint64_t addr); - bool convertUnwindInfo() { return _convertUnwindInfo; } + bool warnUnwindConversionProblems() { return _warnUnwindConversionProblems; } bool hasDataInCodeLabels() { return _hasDataInCodeLabels; } - + bool keepDwarfUnwind() { return _keepDwarfUnwind; } + bool forceDwarfConversion() { return _forceDwarfConversion; } + macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } @@ -989,7 +997,7 @@ public: : sortedSymbolIndexes(ssa), sortedSymbolCount(ssc), cfiStartsArray(cfisa), cfiStartsCount(cfisc), fileHasOverlappingSymbols(ols), newSection(false), cfiIndex(0), symIndex(0) {} - bool next(Parser& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, + bool next(Parser& parser, const Section& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr, pint_t* addr, pint_t* size, const macho_nlist

** sym); pint_t peek(Parser& parser, pint_t startAddr, pint_t endAddr); void beginSection() { newSection = true; symIndex = 0; } @@ -1050,8 +1058,8 @@ private: Parser(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, - ld::File::Ordinal ordinal, bool convertUnwindInfo); + const char* path, time_t modTime, ld::File::Ordinal ordinal, + bool warnUnwindConversionProblems, bool keepDwarfUnwind, bool forceDwarfConversion); ld::relocatable::File* parse(const ParserOptions& opts); uint8_t loadCommandSizeMask(); bool parseLoadCommands(); @@ -1106,8 +1114,10 @@ private: bool _hasLongBranchStubs; bool _AppleObjc; // FSF has objc that uses different data layout bool _overlappingSymbols; - bool _convertUnwindInfo; + bool _warnUnwindConversionProblems; bool _hasDataInCodeLabels; + bool _keepDwarfUnwind; + bool _forceDwarfConversion; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1118,7 +1128,7 @@ private: template Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - ld::File::Ordinal ordinal, bool convertDUI) + ld::File::Ordinal ordinal, bool convertDUI, bool keepDwarfUnwind, bool forceDwarfConversion) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), @@ -1129,7 +1139,8 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL), _tentativeDefinitionCount(0), _absoluteSymbolCount(0), _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), - _overlappingSymbols(false), _convertUnwindInfo(convertDUI), _hasDataInCodeLabels(false), + _overlappingSymbols(false), _warnUnwindConversionProblems(convertDUI), _hasDataInCodeLabels(false), + _keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion), _stubsSectionNum(0), _stubsMachOSection(NULL) { } @@ -1183,6 +1194,19 @@ bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, c } +template <> +bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + if ( header->filetype() != MH_OBJECT ) + return false; + return true; +} + template <> const char* Parser::fileKind(const uint8_t* fileContent) @@ -1222,6 +1246,18 @@ const char* Parser::fileKind(const uint8_t* fileContent) return "arm???"; } +#if SUPPORT_ARCH_arm64 +template <> +const char* Parser::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return NULL; + return "arm64"; +} +#endif template bool Parser::hasObjC2Categories(const uint8_t* fileContent) @@ -1322,7 +1358,7 @@ typename A::P::uint_t Parser::LabelAndCFIBreakIterator::peek(Parser& parse // was becuase of a label, the symbol). Returns false when no more chunks. // template -bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectNum, pint_t startAddr, pint_t endAddr, +bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr, pint_t* addr, pint_t* size, const macho_nlist

** symbol) { // may not be a label on start of section, but need atom demarcation there @@ -1331,10 +1367,12 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectN // advance symIndex until we get to the first label at or past the start of this section while ( symIndex < sortedSymbolCount ) { const macho_nlist

& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]); - pint_t nextSymbolAddr = sym.n_value(); - //fprintf(stderr, "sectNum=%d, nextSymbolAddr=0x%08llX, name=%s\n", sectNum, (uint64_t)nextSymbolAddr, parser.nameFromSymbol(sym)); - if ( (nextSymbolAddr > startAddr) || ((nextSymbolAddr == startAddr) && (sym.n_sect() == sectNum)) ) - break; + if ( ! sect.ignoreLabel(parser.nameFromSymbol(sym)) ) { + pint_t nextSymbolAddr = sym.n_value(); + //fprintf(stderr, "sectNum=%d, nextSymbolAddr=0x%08llX, name=%s\n", sectNum, (uint64_t)nextSymbolAddr, parser.nameFromSymbol(sym)); + if ( (nextSymbolAddr > startAddr) || ((nextSymbolAddr == startAddr) && (sym.n_sect() == sectNum)) ) + break; + } ++symIndex; } if ( symIndex < sortedSymbolCount ) { @@ -1381,7 +1419,19 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectN *symbol = NULL; return true; } - // no symbols left in whole file, so entire section is one chunk + // no symbols in section, check CFI + if ( cfiIndex < cfiStartsCount ) { + pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; + if ( nextCfiAddr < endAddr ) { + // use cfi + ++cfiIndex; + *addr = nextCfiAddr; + *size = peek(parser, startAddr, endAddr) - nextCfiAddr; + *symbol = NULL; + return true; + } + } + // no cfi, so whole section is one chunk *addr = startAddr; *size = endAddr - startAddr; *symbol = NULL; @@ -1459,6 +1509,16 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, uint32_t sectN return false; } +#define STACK_ALLOC_IF_SMALL(_type, _name, _actual_count, _maxCount) \ + _type* _name = NULL; \ + uint32_t _name##_count = 1; \ + if ( _actual_count > _maxCount ) \ + _name = (_type*)malloc(sizeof(_type) * _actual_count); \ + else \ + _name##_count = _actual_count; \ + _type _name##_buffer[_name##_count]; \ + if ( _name == NULL ) \ + _name = _name##_buffer; template @@ -1475,7 +1535,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) if ( ! parseLoadCommands() ) return _file; - // make array of + // make array of uint32_t sortedSectionIndexes[_machOSectionsCount]; this->makeSortedSectionsArray(sortedSectionIndexes); @@ -1491,40 +1551,46 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) uint32_t countOfCUs = 0; if ( _compactUnwindSection != NULL ) countOfCUs = _compactUnwindSection->count(); - uint8_t cuInfoBuffer[sizeof(typename CUSection::Info) * countOfCUs]; - typename CUSection::Info* cuInfoArray = (typename CUSection::Info*)cuInfoBuffer; + // stack allocate (if not too large) cuInfoBuffer + STACK_ALLOC_IF_SMALL(typename CUSection::Info, cuInfoArray, countOfCUs, 1024); if ( countOfCUs != 0 ) _compactUnwindSection->parse(*this, countOfCUs, cuInfoArray); + + // create lists of address that already have compact unwind and thus don't need the dwarf parsed + unsigned cuLsdaCount = 0; + pint_t cuStarts[countOfCUs]; + for (uint32_t i=0; i < countOfCUs; ++i) { + if ( CUSection::encodingMeansUseDwarf(cuInfoArray[i].compactUnwindInfo) ) + cuStarts[i] = -1; + else + cuStarts[i] = cuInfoArray[i].functionStartAddress; + if ( cuInfoArray[i].lsdaAddress != 0 ) + ++cuLsdaCount; + } + // if it exists, do special early parsing of __eh_frame section - // stack allocate array of CFI_Atom_Info + // stack allocate (if not too large) array of CFI_Atom_Info uint32_t countOfCFIs = 0; if ( _EHFrameSection != NULL ) countOfCFIs = _EHFrameSection->cfiCount(); - typename CFISection::CFI_Atom_Info cfiArray[countOfCFIs]; + STACK_ALLOC_IF_SMALL(typename CFISection::CFI_Atom_Info, cfiArray, countOfCFIs, 1024); + // stack allocate (if not too large) a copy of __eh_frame to apply relocations to - uint8_t* ehBuffer = NULL; - uint32_t stackAllocSize = 0; - if ( (countOfCFIs != 0) && _EHFrameSection->needsRelocating() ) { - uint32_t sectSize = _EHFrameSection->machoSection()->size(); - if ( sectSize > 50*1024 ) - ehBuffer = (uint8_t*)malloc(sectSize); - else - stackAllocSize = sectSize; - } - uint32_t ehStackBuffer[1+stackAllocSize/4]; // make 4-byte aligned stack bufffer - if ( ehBuffer == NULL ) - ehBuffer = (uint8_t*)&ehStackBuffer; + uint32_t sectSize = 4; + if ( (countOfCFIs != 0) && _EHFrameSection->needsRelocating() ) + sectSize = _EHFrameSection->machoSection()->size()+4; + STACK_ALLOC_IF_SMALL(uint8_t, ehBuffer, sectSize, 50*1024); uint32_t cfiStartsCount = 0; if ( countOfCFIs != 0 ) { - _EHFrameSection->cfiParse(*this, ehBuffer, cfiArray, countOfCFIs); + _EHFrameSection->cfiParse(*this, ehBuffer, cfiArray, countOfCFIs, cuStarts, countOfCUs); // count functions and lsdas for(uint32_t i=0; i < countOfCFIs; ++i) { if ( cfiArray[i].isCIE ) continue; - //fprintf(stderr, "cfiArray[i].func = 0x%08llX, cfiArray[i].lsda = 0x%08llX, encoding=0x%08X\n", - // (uint64_t)cfiArray[i].u.fdeInfo.function.targetAddress, - // (uint64_t)cfiArray[i].u.fdeInfo.lsda.targetAddress, + //fprintf(stderr, "cfiArray[i].func = 0x%08llX, cfiArray[i].lsda = 0x%08llX, encoding=0x%08X\n", + // (uint64_t)cfiArray[i].u.fdeInfo.function.targetAddress, + // (uint64_t)cfiArray[i].u.fdeInfo.lsda.targetAddress, // cfiArray[i].u.fdeInfo.compactUnwindInfo); if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) ++cfiStartsCount; @@ -1535,23 +1601,41 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) CFI_CU_InfoArrays cfis(cfiArray, countOfCFIs, cuInfoArray, countOfCUs); // create sorted array of function starts and lsda starts - pint_t cfiStartsArray[cfiStartsCount]; + pint_t cfiStartsArray[cfiStartsCount+cuLsdaCount]; uint32_t countOfFDEs = 0; + uint32_t cfiStartsArrayCount = 0; if ( countOfCFIs != 0 ) { - int index = 0; for(uint32_t i=0; i < countOfCFIs; ++i) { if ( cfiArray[i].isCIE ) continue; if ( cfiArray[i].u.fdeInfo.function.targetAddress != CFI_INVALID_ADDRESS ) - cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.function.targetAddress; + cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.function.targetAddress; if ( cfiArray[i].u.fdeInfo.lsda.targetAddress != CFI_INVALID_ADDRESS ) - cfiStartsArray[index++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; + cfiStartsArray[cfiStartsArrayCount++] = cfiArray[i].u.fdeInfo.lsda.targetAddress; ++countOfFDEs; } - ::qsort(cfiStartsArray, cfiStartsCount, sizeof(pint_t), pointerSorter); + } + if ( cuLsdaCount != 0 ) { + // merge in an lsda info from compact unwind + for (uint32_t i=0; i < countOfCUs; ++i) { + if ( cuInfoArray[i].lsdaAddress == 0 ) + continue; + // append to cfiStartsArray if not already in that list + bool found = false; + for(uint32_t j=0; j < cfiStartsArrayCount; ++j) { + if ( cfiStartsArray[j] == cuInfoArray[i].lsdaAddress ) + found = true; + } + if ( ! found ) { + cfiStartsArray[cfiStartsArrayCount++] = cuInfoArray[i].lsdaAddress; + } + } + } + if ( cfiStartsArrayCount != 0 ) { + ::qsort(cfiStartsArray, cfiStartsArrayCount, sizeof(pint_t), pointerSorter); #ifndef NDEBUG // scan for FDEs claming the same function - for(int i=1; i < index; ++i) { + for(uint32_t i=1; i < cfiStartsArrayCount; ++i) { assert( cfiStartsArray[i] != cfiStartsArray[i-1] ); } #endif @@ -1562,7 +1646,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // figure out how many atoms will be allocated and allocate LabelAndCFIBreakIterator breakIterator(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, - cfiStartsCount, _overlappingSymbols); + cfiStartsArrayCount, _overlappingSymbols); uint32_t computedAtomCount = 0; for (uint32_t i=0; i < sectionsCount; ++i ) { breakIterator.beginSection(); @@ -1577,7 +1661,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // have each section append atoms to _atomsArray LabelAndCFIBreakIterator breakIterator2(sortedSymbolIndexes, _symbolsInSections, cfiStartsArray, - cfiStartsCount, _overlappingSymbols); + cfiStartsArrayCount, _overlappingSymbols); for (uint32_t i=0; i < sectionsCount; ++i ) { uint8_t* atoms = _file->_atomsArray + _file->_atomsArrayCount*sizeof(Atom); breakIterator2.beginSection(); @@ -1628,6 +1712,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) _file->_unwindInfos.push_back(info); Atom* func = findAtomByAddress(cfiArray[i].u.fdeInfo.function.targetAddress); func->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); + //fprintf(stderr, "cu from dwarf =0x%08X, atom=%s\n", info.unwindInfo, func->name()); } } // apply compact infos in __LD,__compact_unwind section to each function @@ -1639,22 +1724,26 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) assert(info->function != NULL); ld::Atom::UnwindInfo ui; ui.startOffset = info->functionStartAddress - info->function->objectAddress(); - ui.unwindInfo = info->compactUnwindInfo; + ui.unwindInfo = info->compactUnwindInfo; _file->_unwindInfos.push_back(ui); - // if previous is for same function, extend range - if ( info->function == lastFunc ) { - if ( lastEnd != ui.startOffset ) { - if ( lastEnd < ui.startOffset ) - warning("__LD,__compact_unwind entries for %s have a gap at offset 0x%0X", info->function->name(), lastEnd); - else - warning("__LD,__compact_unwind entries for %s overlap at offset 0x%0X", info->function->name(), lastEnd); + // don't override with converted cu with "use dwarf" cu, if forcing dwarf conversion + if ( !_forceDwarfConversion || !CUSection::encodingMeansUseDwarf(info->compactUnwindInfo) ) { + //fprintf(stderr, "cu=0x%08X, atom=%s\n", ui.unwindInfo, info->function->name()); + // if previous is for same function, extend range + if ( info->function == lastFunc ) { + if ( lastEnd != ui.startOffset ) { + if ( lastEnd < ui.startOffset ) + warning("__LD,__compact_unwind entries for %s have a gap at offset 0x%0X", info->function->name(), lastEnd); + else + warning("__LD,__compact_unwind entries for %s overlap at offset 0x%0X", info->function->name(), lastEnd); + } + lastFunc->extendUnwindInfoRange(); } - lastFunc->extendUnwindInfoRange(); + else + info->function->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); + lastFunc = info->function; + lastEnd = ui.startOffset + info->rangeLength; } - else - info->function->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1); - lastFunc = info->function; - lastEnd = ui.startOffset + info->rangeLength; } // parse dwarf debug info to get line info @@ -1668,6 +1757,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } +template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template bool Parser::parseLoadCommands() @@ -1738,6 +1828,21 @@ bool Parser::parseLoadCommands() if ( _dataInCodeEnd > (macho_data_in_code_entry

*)endOfFile ) throw "LC_DATA_IN_CODE table extends beyond end of file"; } + break; + case LC_LINKER_OPTION: + { + const macho_linker_option_command

* loc = (macho_linker_option_command

*)cmd; + const char* buffer = loc->buffer(); + _file->_linkerOptions.resize(_file->_linkerOptions.size() + 1); + std::vector& vec = _file->_linkerOptions.back(); + for (uint32_t j=0; j < loc->count(); ++j) { + vec.push_back(buffer); + buffer += strlen(buffer) + 1; + } + if ( buffer > ((char*)cmd + loc->cmdsize()) ) + throw "malformed LC_LINKER_OPTION"; + } + break; default: if ( cmd->cmd() == macho_segment_command

::CMD ) { if ( segment != NULL ) @@ -1984,7 +2089,6 @@ void Parser::makeSortedSymbolsArray(uint32_t array[], const uint32_t sectionA } } - template void Parser::makeSections() { @@ -2034,6 +2138,7 @@ void Parser::makeSections() // }; // #define OBJC_IMAGE_SUPPORTS_GC 2 // #define OBJC_IMAGE_GC_ONLY 4 + // #define OBJC_IMAGE_IS_SIMULATED 32 // const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset()); if ( (sect->size() >= 8) && (contents[0] == 0) ) { @@ -2042,6 +2147,8 @@ void Parser::makeSections() _file->_objConstraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) _file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( (flags & 32) == 32 ) + _file->_objConstraint = ld::File::objcConstraintRetainReleaseForSimulator; else _file->_objConstraint = ld::File::objcConstraintRetainRelease; if ( sect->size() > 8 ) { @@ -2463,8 +2570,14 @@ const char* Parser::scanSymbolTableForAddress(uint64_t addr) continue; // return with exact match - if ( sym.n_value() == addr ) - return nameFromSymbol(sym); + if ( sym.n_value() == addr ) { + const char* name = nameFromSymbol(sym); + if ( strncmp(name, "ltmp", 4) != 0 ) + return name; + // treat 'ltmp*' labels as close match + closestSymAddr = sym.n_value(); + closestSymName = name; + } // record closest seen so far if ( (sym.n_value() < addr) && ((sym.n_value() > closestSymAddr) || (closestSymName == NULL)) ) @@ -2519,6 +2632,23 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co case ld::Fixup::kindStoreThumbBranch22: firstKind = ld::Fixup::kindStoreTargetAddressThumbBranch22; break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Branch26: + firstKind = ld::Fixup::kindStoreTargetAddressARM64Branch26; + break; + case ld::Fixup::kindStoreARM64Page21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64Page21; + break; + case ld::Fixup::kindStoreARM64PageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64PageOff12; + break; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21; + break; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12; + break; +#endif default: combined = false; cl = ld::Fixup::k1of2; @@ -2537,6 +2667,10 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co // backing string in CFStrings should always be direct addFixup(src, cl, firstKind, target.atom); } + else if ( (src.atom == target.atom) && (target.atom->combine() == ld::Atom::combineByName) ) { + // reference to self should always be direct + addFixup(src, cl, firstKind, target.atom); + } else { // change direct fixup to by-name fixup addFixup(src, cl, firstKind, false, target.atom->name()); @@ -2922,6 +3056,22 @@ bool Parser::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t sz = 4; break; + case DW_FORM_sec_offset: + sz = sizeof(typename A::P::uint_t); + break; + + case DW_FORM_exprloc: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_flag_present: + sz = 0; + break; + + case DW_FORM_ref_sig8: + sz = 8; + break; + default: return false; } @@ -3723,7 +3873,7 @@ template void CFISection::warnFunc(void* ref, uint64_t funcAddr, const char* msg) { Parser* parser = (Parser*)ref; - if ( ! parser->convertUnwindInfo() ) + if ( ! parser->warnUnwindConversionProblems() ) return; if ( funcAddr != CFI_INVALID_ADDRESS ) { // atoms are not constructed yet, so scan symbol table for labels @@ -3741,6 +3891,12 @@ bool CFISection::needsRelocating() return true; } +template <> +bool CFISection::needsRelocating() +{ + return true; +} + template bool CFISection::needsRelocating() { @@ -3748,9 +3904,9 @@ bool CFISection::needsRelocating() } template <> -void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // copy __eh_frame data to buffer memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); @@ -3796,7 +3952,6 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, } } - // create ObjectAddressSpace object for use by libunwind OAS oas(*this, buffer); @@ -3804,7 +3959,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::parseCFIs( oas, this->_machOSection->addr(), this->_machOSection->size(), - cfiArray, count, (void*)&parser, warnFunc); + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), cfiArray, count, (void*)&parser, warnFunc); if ( msg != NULL ) throwf("malformed __eh_frame section: %s", msg); } @@ -3812,7 +3967,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // create ObjectAddressSpace object for use by libunwind OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); @@ -3821,7 +3976,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::parseCFIs( oas, this->_machOSection->addr(), this->_machOSection->size(), - cfiArray, count, (void*)&parser, warnFunc); + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), cfiArray, count, (void*)&parser, warnFunc); if ( msg != NULL ) throwf("malformed __eh_frame section: %s", msg); } @@ -3832,12 +3987,76 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // arm does not use zero cost exceptions assert(count == 0); } +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) +{ + // copy __eh_frame data to buffer + memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); + + // and apply relocations + const macho_relocation_info

* relocs = (macho_relocation_info

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

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { + uint64_t* p64 = (uint64_t*)&buffer[reloc->r_address()]; + uint32_t* p32 = (uint32_t*)&buffer[reloc->r_address()]; + uint32_t addend32 = E::get32(*p32); + uint64_t addend64 = E::get64(*p64); + uint64_t value = 0; + switch ( reloc->r_type() ) { + case ARM64_RELOC_SUBTRACTOR: + value = 0 - parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + ++reloc; + if ( reloc->r_extern() ) + value += parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case ARM64_RELOC_UNSIGNED: + value = parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); + break; + case ARM64_RELOC_POINTER_TO_GOT: + // this is used for the reference to the personality function in CIEs + // store the symbol number of the personality function for later use as a Fixup + value = reloc->r_symbolnum(); + addend32 = 0; + addend64 = 0; + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation type at r_address=0x%08X\n", reloc->r_address()); + break; + } + switch ( reloc->r_length() ) { + case 3: + E::set64(*p64, value + addend64); + break; + case 2: + E::set32(*p32, value + addend32); + break; + default: + fprintf(stderr, "CFISection::cfiParse() unexpected relocation size at r_address=0x%08X\n", reloc->r_address()); + break; + } + } + + + // create ObjectAddressSpace object for use by libunwind + OAS oas(*this, buffer); + + // use libuwind to parse __eh_frame data into array of CFI_Atom_Info + const char* msg; + msg = libunwind::DwarfInstructions::parseCFIs( + oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), + cfiArray, count, (void*)&parser, warnFunc); + if ( msg != NULL ) + throwf("malformed __eh_frame section: %s", msg); +} template @@ -3875,6 +4094,7 @@ uint32_t CFISection::appendAtoms(class Parser& parser, uint8_t* p, template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } template <> bool CFISection::bigEndian() { return false; } +template <> bool CFISection::bigEndian() { return false; } template <> @@ -3924,11 +4144,36 @@ void CFISection::addCiePersonalityFixups(class Parser& parser, const C } + +#if SUPPORT_ARCH_arm64 +template <> +void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) +{ + uint8_t personalityEncoding = cieInfo->u.cieInfo.personality.encodingOfTargetAddress; + if ( personalityEncoding == 0x9B ) { + // compiler always produces ARM64_RELOC_GOT r_pcrel=1 to personality function + // CFISection::cfiParse() set targetAddress to be symbolIndex + addressInCIE + uint32_t symbolIndex = cieInfo->u.cieInfo.personality.targetAddress + - cieInfo->address - cieInfo->u.cieInfo.personality.offsetInCFI; + const macho_nlist

& sym = parser.symbolFromIndex(symbolIndex); + const char* personalityName = parser.nameFromSymbol(sym); + + Atom* cieAtom = this->findAtomByAddress(cieInfo->address); + Parser::SourceLocation src(cieAtom, cieInfo->u.cieInfo.personality.offsetInCFI); + parser.addFixup(src, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, false, personalityName); + parser.addFixup(src, ld::Fixup::k2of2, ld::Fixup::kindStoreARM64PCRelToGOT); + } + else if ( personalityEncoding != 0 ) { + throwf("unsupported address encoding (%02X) of personality function in CIE", + personalityEncoding); + } +} +#endif + template void CFISection::addCiePersonalityFixups(class Parser& parser, const CFI_Atom_Info* cieInfo) { - // FIX ME - assert(0); + assert(0 && "addCiePersonalityFixups() not implemented for arch"); } template @@ -4162,27 +4407,103 @@ typename A::P::uint_t CFISection::OAS::getEncodedP(pint_t& addr, pint_t end, template <> const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) { - assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section"); - assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section"); - const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); - return parser.nameFromSymbol(sym); + if ( reloc->r_extern() ) { + assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + return parser.nameFromSymbol(sym); + } + else { + const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); + pint_t personalityAddr = *content; + Section* personalitySection = parser.sectionForAddress(personalityAddr); + assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); + // atoms may not be constructed yet, so scan symbol table for labels + const char* name = parser.scanSymbolTableForAddress(personalityAddr); + return name; + } } template <> const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) { - assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section"); - assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section"); - const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); - return parser.nameFromSymbol(sym); + if ( reloc->r_extern() ) { + assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + return parser.nameFromSymbol(sym); + } + else { + // support __LD, __compact_unwind personality entries which are pointer to personality non-lazy pointer + const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); + pint_t nlPointerAddr = *content; + Section* nlSection = parser.sectionForAddress(nlPointerAddr); + if ( nlSection->type() == ld::Section::typeCode ) { + // personality function is defined in this .o file, so this is a direct reference to it + // atoms may not be constructed yet, so scan symbol table for labels + const char* name = parser.scanSymbolTableForAddress(nlPointerAddr); + return name; + } + else { + uint32_t symIndex = parser.symbolIndexFromIndirectSectionAddress(nlPointerAddr, nlSection->machoSection()); + const macho_nlist

& nlSymbol = parser.symbolFromIndex(symIndex); + return parser.nameFromSymbol(nlSymbol); + } + } } +#if SUPPORT_ARCH_arm64 +template <> +const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info* reloc) +{ + if ( reloc->r_extern() ) { + assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + return parser.nameFromSymbol(sym); + } + else { + const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); + pint_t personalityAddr = *content; + Section* personalitySection = parser.sectionForAddress(personalityAddr); + assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); + // atoms may not be constructed yet, so scan symbol table for labels + const char* name = parser.scanSymbolTableForAddress(personalityAddr); + return name; + } +} +#endif + template const char* CUSection::personalityName(class Parser& parser, const macho_relocation_info

* reloc) { return NULL; } +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF); +} + +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); +} + +#if SUPPORT_ARCH_arm_any +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return false; +} +#endif + +#if SUPPORT_ARCH_arm64 +template <> +bool CUSection::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF); +} +#endif template int CUSection::infoSorter(const void* l, const void* r) @@ -4219,8 +4540,7 @@ void CUSection::parse(class Parser& parser, uint32_t cnt, Info array[]) } } - // scan relocs, local relocs are useless - ignore them - // extern relocs are needed for personality references (possibly for function/lsda refs??) + // scan relocs, extern relocs are needed for personality references (possibly for function/lsda refs??) const macho_relocation_info

* relocs = (macho_relocation_info

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

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

* reloc = relocs; reloc < relocsEnd; ++reloc) { @@ -4241,11 +4561,18 @@ void CUSection::parse(class Parser& parser, uint32_t cnt, Info array[]) else if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry

)) == macho_compact_unwind_entry

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

); array[entryIndex].functionSymbolIndex = reloc->r_symbolnum(); + array[entryIndex].functionStartAddress += parser.symbolFromIndex(reloc->r_symbolnum()).n_value(); } else { warning("unexpected extern relocation in __compact_unwind section"); } } + else { + if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry

)) == macho_compact_unwind_entry

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

); + array[entryIndex].personality = this->personalityName(parser, reloc); + } + } } // sort array by function start address so unwind infos will be contiguous for a given function @@ -4268,9 +4595,6 @@ void CUSection::makeFixups(class Parser& parser, const struct Parser::C Info* const arrayStart = cus.cuArray; Info* const arrayEnd = &cus.cuArray[cus.cuCount]; for (Info* info=arrayStart; info < arrayEnd; ++info) { - // if external reloc was used, real address is symbol n_value + addend - if ( info->functionSymbolIndex != 0xFFFFFFFF ) - info->functionStartAddress += parser.symbolFromIndex(info->functionSymbolIndex).n_value(); // find function atom from address info->function = parser.findAtomByAddress(info->functionStartAddress); // find lsda atom from address @@ -4356,7 +4680,7 @@ uint32_t SymboledSection::computeAtomCount(class Parser& parser, pint_t addr; pint_t size; const macho_nlist

* sym; - while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &sym) ) { + while ( it.next(parser, *this, sectNum, startAddr, endAddr, &addr, &size, &sym) ) { ++count; } //fprintf(stderr, "computeAtomCount(%s,%s) => %d\n", this->segmentName(), this->sectionName(), count); @@ -4365,7 +4689,7 @@ uint32_t SymboledSection::computeAtomCount(class Parser& parser, template uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, - struct Parser::LabelAndCFIBreakIterator& it, + struct Parser::LabelAndCFIBreakIterator& it, const struct Parser::CFI_CU_InfoArrays&) { this->_beginAtoms = (Atom*)p; @@ -4379,7 +4703,7 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, pint_t addr; pint_t size; const macho_nlist

* label; - while ( it.next(parser, sectNum, startAddr, endAddr, &addr, &size, &label) ) { + while ( it.next(parser, *this, sectNum, startAddr, endAddr, &addr, &size, &label) ) { Atom* allocatedSpace = (Atom*)p; // is break because of label or CFI? if ( label != NULL ) { @@ -4396,7 +4720,7 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, ld::Atom::ContentType ctype = this->contentType(); if ( ctype == ld::Atom::typeLSDA ) inclusion = ld::Atom::symbolTableInWithRandomAutoStripLabel; - new (allocatedSpace) Atom(*this, "anon", addr, size, ld::Atom::definitionRegular, ld::Atom::combineNever, + new (allocatedSpace) Atom(*this, "anon", addr, size, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, ctype, inclusion, this->dontDeadStrip(), false, false, this->alignmentForAddress(addr)); } @@ -4409,6 +4733,19 @@ uint32_t SymboledSection::appendAtoms(class Parser& parser, uint8_t* p, } +template <> +ld::Atom::SymbolTableInclusion ImplicitSizeSection::symbolTableInclusion() +{ + return ld::Atom::symbolTableInWithRandomAutoStripLabel; +} + +template +ld::Atom::SymbolTableInclusion ImplicitSizeSection::symbolTableInclusion() +{ + return ld::Atom::symbolTableNotIn; +} + + template uint32_t ImplicitSizeSection::computeAtomCount(class Parser& parser, struct Parser::LabelAndCFIBreakIterator& it, @@ -4426,15 +4763,18 @@ uint32_t ImplicitSizeSection::computeAtomCount(class Parser& parser, // if there are multiple labels in this section for the same address, then clone them into multi atoms pint_t prevSymbolAddr = (pint_t)(-1); uint8_t prevSymbolSectNum = 0; + bool prevIgnore = false; for(uint32_t i=0; i < it.sortedSymbolCount; ++i) { const macho_nlist

& sym = parser.symbolFromIndex(it.sortedSymbolIndexes[i]); const pint_t symbolAddr = sym.n_value(); - const pint_t symbolSectNum = sym.n_sect(); - if ( (symbolAddr == prevSymbolAddr) && (prevSymbolSectNum == symbolSectNum) && (symbolSectNum == this->sectionNum(parser)) ) { + const uint8_t symbolSectNum = sym.n_sect(); + const bool ignore = this->ignoreLabel(parser.nameFromSymbol(sym)); + if ( !ignore && !prevIgnore && (symbolAddr == prevSymbolAddr) && (prevSymbolSectNum == symbolSectNum) && (symbolSectNum == this->sectionNum(parser)) ) { ++count; } prevSymbolAddr = symbolAddr; prevSymbolSectNum = symbolSectNum; + prevIgnore = ignore; } } return count; @@ -4457,33 +4797,44 @@ uint32_t ImplicitSizeSection::appendAtoms(class Parser& parser, uint8_t* p pint_t size; const macho_nlist

* foundLabel; Atom* allocatedSpace; - while ( it.next(parser, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { + while ( it.next(parser, *this, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { if ( foundLabel != NULL ) { + bool skip = false; pint_t labeledAtomSize = this->elementSizeAtAddress(foundAddr); allocatedSpace = (Atom*)p; if ( this->ignoreLabel(parser.nameFromSymbol(*foundLabel)) ) { - //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)foundAddr); - new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, + if ( size == 0 ) { + // + // a size of zero means there is another label at same location + // and we are supposed to ignore this label + skip = true; + } + else { + //fprintf(stderr, " 0x%08llX make annon, size=%lld\n", (uint64_t)foundAddr, (uint64_t)size); + new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, this->elementSizeAtAddress(foundAddr), this->definition(), this->combine(parser, foundAddr), this->scopeAtAddress(parser, foundAddr), this->contentType(), this->symbolTableInclusion(), this->dontDeadStrip(), false, false, this->alignmentForAddress(foundAddr)); + } } else { // make named atom for label //fprintf(stderr, " 0x%08llX make labeled\n", (uint64_t)foundAddr); new (allocatedSpace) Atom(*this, parser, *foundLabel, labeledAtomSize); } - ++count; - p += sizeof(Atom); - foundAddr += labeledAtomSize; - size -= labeledAtomSize; + if ( !skip ) { + ++count; + p += sizeof(Atom); + foundAddr += labeledAtomSize; + size -= labeledAtomSize; + } } // some number of anonymous atoms for (pint_t addr = foundAddr; addr < (foundAddr+size); addr += elementSizeAtAddress(addr) ) { // make anon atoms for area before label if ( this->useElementAt(parser, it, addr) ) { - //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)addr); + //fprintf(stderr, " 0x%08llX make annon, size=%lld\n", (uint64_t)addr, (uint64_t)elementSizeAtAddress(addr)); allocatedSpace = (Atom*)p; new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, addr), addr, this->elementSizeAtAddress(addr), this->definition(), this->combine(parser, addr), this->scopeAtAddress(parser, addr), @@ -4606,11 +4957,12 @@ bool CStringSection::useElementAt(Parser& parser, struct Parser::LabelA } template -bool CStringSection::ignoreLabel(const char* label) +bool CStringSection::ignoreLabel(const char* label) const { return (label[0] == 'L') || (label[0] == 'l'); } + template Atom* CStringSection::findAtomByAddress(pint_t addr) { @@ -4665,6 +5017,12 @@ ld::Fixup::Kind NonLazyPointerSection::fixupKind() return ld::Fixup::kindStoreLittleEndian32; } +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreLittleEndian64; +} + template <> void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -5056,12 +5414,16 @@ const char* PointerToCStringSection::targetCString(const class Atom* atom, case ld::Fixup::bindingsIndirectlyBound: targetAtom = ind.indirectAtom(fit->u.bindingIndex); break; + case ld::Fixup::bindingDirectlyBound: + targetAtom = fit->u.target; + break; default: - assert(0); + assert(0 && "unsupported reference to selector"); } assert(targetAtom != NULL); const Atom* target = dynamic_cast*>(targetAtom); - assert(target != NULL); + assert(target != NULL); + assert(target->contentType() == ld::Atom::typeCString); return (char*)target->contentPointer(); } @@ -6054,8 +6416,314 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati #endif - - +#if SUPPORT_ARCH_arm64 +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) +{ + bool result = false; + Parser::SourceLocation src; + Parser::TargetDesc target = { NULL, NULL, false, 0 }; + Parser::TargetDesc toTarget; + int32_t prefixRelocAddend = 0; + if ( reloc->r_type() == ARM64_RELOC_ADDEND ) { + uint32_t rawAddend = reloc->r_symbolnum(); + prefixRelocAddend = rawAddend; + if ( rawAddend & 0x00800000 ) + prefixRelocAddend |= 0xFF000000; // sign extend 24-bit signed int to 32-bits + uint32_t addendAddress = reloc->r_address(); + ++reloc; //advance to next reloc record + result = true; + if ( reloc->r_address() != addendAddress ) + throw "ARM64_RELOC_ADDEND r_address does not match next reloc's r_address"; + } + const macho_section

* sect = this->machoSection(); + uint64_t srcAddr = sect->addr() + reloc->r_address(); + src.atom = this->findAtomByAddress(srcAddr); + src.offsetInAtom = srcAddr - src.atom->_objAddress; + const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); + uint64_t contentValue = 0; + const macho_relocation_info* nextReloc = &reloc[1]; + bool useDirectBinding; + uint32_t instruction; + uint32_t encodedAddend; + switch ( reloc->r_length() ) { + case 0: + contentValue = *fixUpPtr; + break; + case 1: + contentValue = (int64_t)(int16_t)E::get16(*((uint16_t*)fixUpPtr)); + break; + case 2: + contentValue = (int64_t)(int32_t)E::get32(*((uint32_t*)fixUpPtr)); + break; + case 3: + contentValue = E::get64(*((uint64_t*)fixUpPtr)); + break; + } + if ( reloc->r_extern() ) { + const macho_nlist

& sym = parser.symbolFromIndex(reloc->r_symbolnum()); + const char* symbolName = parser.nameFromSymbol(sym); + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (symbolName[0] == 'L') || (symbolName[0] == 'l')) ) { + // use direct reference for local symbols + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + //target.addend += contentValue; + } + else if ( ((sym.n_type() & N_TYPE) == N_SECT) && (src.atom->_objAddress <= sym.n_value()) && (sym.n_value() < (src.atom->_objAddress+src.atom->size())) ) { + // spurious warning when weak function has reference to itself + // use direct reference when atom targets itself + target.atom = src.atom; + target.name = NULL; + } + else { + target.name = symbolName; + target.weakImport = parser.weakImportFromSymbol(sym); + //target.addend = contentValue; + } + // cfstrings should always use direct reference to backing store + if ( (this->type() == ld::Section::typeCFString) && (src.offsetInAtom != 0) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), target); + //target.addend = contentValue; + } + } + else { + if ( reloc->r_pcrel() ) + contentValue += srcAddr; + parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target); + } + switch ( reloc->r_type() ) { + case ARM64_RELOC_UNSIGNED: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_UNSIGNED not supported"; + target.addend = contentValue; + switch ( reloc->r_length() ) { + case 0: + case 1: + throw "length < 2 and ARM64_RELOC_UNSIGNED not supported"; + case 2: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target); + break; + case 3: + parser.addFixups(src, ld::Fixup::kindStoreLittleEndian64, target); + break; + } + break; + case ARM64_RELOC_BRANCH26: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_BRANCH26 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_BRANCH26 not supported"; + if ( reloc->r_length() != 2 ) + throw "r_length != 2 and ARM64_RELOC_BRANCH26 not supported"; + if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_probe$", 16) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreARM64DtraceCallSiteNop, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[16]); + } + else if ( (target.name != NULL) && (strncmp(target.name, "___dtrace_isenabled$", 20) == 0) ) { + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear, false, target.name); + parser.addDtraceExtraInfos(src, &target.name[20]); + } + else { + target.addend = prefixRelocAddend; + instruction = contentValue; + encodedAddend = (instruction & 0x03FFFFFF) << 2; + if ( encodedAddend != 0 ) { + if ( prefixRelocAddend == 0 ) { + warning("branch26 instruction at 0x%08X has embedded addend. ARM64_RELOC_ADDEND should be used instead", reloc->r_address()); + target.addend = encodedAddend; + } + else { + throwf("branch26 instruction at 0x%08X has embedded addend and ARM64_RELOC_ADDEND also used", reloc->r_address()); + } + } + parser.addFixups(src, ld::Fixup::kindStoreARM64Branch26, target); + } + break; + case ARM64_RELOC_PAGE21: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_PAGE21 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_PAGE21 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_PAGE21 not supported"; + target.addend = prefixRelocAddend; + instruction = contentValue; + encodedAddend = ((instruction & 0x60000000) >> 29) | ((instruction & 0x01FFFFE0) >> 3); + encodedAddend *= 4096; // internally addend is in bytes, so scale + if ( encodedAddend != 0 ) { + if ( prefixRelocAddend == 0 ) { + warning("adrp instruction at 0x%08X has embedded addend. ARM64_RELOC_ADDEND should be used instead", reloc->r_address()); + target.addend = encodedAddend; + } + else { + throwf("adrp instruction at 0x%08X has embedded addend and ARM64_RELOC_ADDEND also used", reloc->r_address()); + } + } + parser.addFixups(src, ld::Fixup::kindStoreARM64Page21, target); + break; + case ARM64_RELOC_PAGEOFF12: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_PAGEOFF12 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_PAGEOFF12 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_PAGEOFF12 not supported"; + target.addend = prefixRelocAddend; + instruction = contentValue; + encodedAddend = ((instruction & 0x003FFC00) >> 10); + // internally addend is in bytes. Some instructions have an implicit scale factor + if ( (instruction & 0x3B000000) == 0x39000000 ) { + switch ( instruction & 0xC0000000 ) { + case 0x00000000: + break; + case 0x40000000: + encodedAddend *= 2; + break; + case 0x80000000: + encodedAddend *= 4; + break; + case 0xC0000000: + encodedAddend *= 8; + break; + } + } + if ( encodedAddend != 0 ) { + if ( prefixRelocAddend == 0 ) { + warning("pageoff12 instruction at 0x%08X has embedded addend. ARM64_RELOC_ADDEND should be used instead", reloc->r_address()); + target.addend = encodedAddend; + } + else { + throwf("pageoff12 instruction at 0x%08X has embedded addend and ARM64_RELOC_ADDEND also used", reloc->r_address()); + } + } + parser.addFixups(src, ld::Fixup::kindStoreARM64PageOff12, target); + break; + case ARM64_RELOC_GOT_LOAD_PAGE21: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_GOT_LOAD_PAGE21 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x60000000) >> 29) | ((instruction & 0x01FFFFE0) >> 3); + if ( target.addend != 0 ) + throw "non-zero addend with ARM64_RELOC_GOT_LOAD_PAGE21 is not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64GOTLoadPage21, target); + break; + case ARM64_RELOC_GOT_LOAD_PAGEOFF12: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_GOT_LOAD_PAGEOFF12 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x003FFC00) >> 10); + parser.addFixups(src, ld::Fixup::kindStoreARM64GOTLoadPageOff12, target); + break; + case ARM64_RELOC_TLVP_LOAD_PAGE21: + if ( ! reloc->r_pcrel() ) + throw "not pcrel and ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_TLVP_LOAD_PAGE21 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x60000000) >> 29) | ((instruction & 0x01FFFFE0) >> 3); + if ( target.addend != 0 ) + throw "non-zero addend with ARM64_RELOC_GOT_LOAD_PAGE21 is not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64TLVPLoadPage21, target); + break; + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + if ( reloc->r_pcrel() ) + throw "pcrel and ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + if ( reloc->r_length() != 2 ) + throw "length != 2 and ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_TLVP_LOAD_PAGEOFF12 not supported"; + instruction = contentValue; + target.addend = ((instruction & 0x003FFC00) >> 10); + parser.addFixups(src, ld::Fixup::kindStoreARM64TLVPLoadPageOff12, target); + break; + case ARM64_RELOC_SUBTRACTOR: + if ( reloc->r_pcrel() ) + throw "ARM64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( reloc->r_length() < 2 ) + throw "ARM64_RELOC_SUBTRACTOR must have r_length of 2 or 3"; + if ( !reloc->r_extern() ) + throw "ARM64_RELOC_SUBTRACTOR must have r_extern=1"; + if ( nextReloc->r_type() != ARM64_RELOC_UNSIGNED ) + throw "ARM64_RELOC_SUBTRACTOR must be followed by ARM64_RELOC_UNSIGNED"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_SUBTRACTOR not supported"; + result = true; + if ( nextReloc->r_pcrel() ) + throw "ARM64_RELOC_UNSIGNED following a ARM64_RELOC_SUBTRACTOR cannot be pc-relative"; + if ( nextReloc->r_length() != reloc->r_length() ) + throw "ARM64_RELOC_UNSIGNED following a ARM64_RELOC_SUBTRACTOR must have same r_length"; + if ( nextReloc->r_extern() ) { + const macho_nlist

& sym = parser.symbolFromIndex(nextReloc->r_symbolnum()); + // use direct reference for local symbols + if ( ((sym.n_type() & N_TYPE) == N_SECT) && (((sym.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(sym)[0] == 'L')) ) { + parser.findTargetFromAddressAndSectionNum(sym.n_value(), sym.n_sect(), toTarget); + toTarget.addend = contentValue; + useDirectBinding = true; + } + else { + toTarget.name = parser.nameFromSymbol(sym); + toTarget.weakImport = parser.weakImportFromSymbol(sym); + toTarget.addend = contentValue; + useDirectBinding = false; + } + } + else { + parser.findTargetFromAddressAndSectionNum(contentValue, nextReloc->r_symbolnum(), toTarget); + useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit); + } + if ( useDirectBinding ) + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); + else + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.weakImport, toTarget.name); + parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, toTarget.addend); + if ( target.atom == NULL ) + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, false, target.name); + else + parser.addFixup(src, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, target.atom); + if ( reloc->r_length() == 2 ) + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian32); + else + parser.addFixup(src, ld::Fixup::k4of4, ld::Fixup::kindStoreLittleEndian64); + break; + case ARM64_RELOC_POINTER_TO_GOT: + if ( ! reloc->r_extern() ) + throw "r_extern == 0 and ARM64_RELOC_POINTER_TO_GOT not supported"; + if ( prefixRelocAddend != 0 ) + throw "ARM64_RELOC_ADDEND followed by ARM64_RELOC_POINTER_TO_GOT not supported"; + if ( reloc->r_pcrel() ) { + if ( reloc->r_length() != 2 ) + throw "r_length != 2 and r_extern = 1 and ARM64_RELOC_POINTER_TO_GOT not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64PCRelToGOT, target); + } + else { + if ( reloc->r_length() != 3 ) + throw "r_length != 3 and r_extern = 0 and ARM64_RELOC_POINTER_TO_GOT not supported"; + parser.addFixups(src, ld::Fixup::kindStoreARM64PointerToGOT, target); + } + break; + default: + throwf("unknown relocation type %d", reloc->r_type()); + } + return result; +} +#endif template bool ObjC1ClassSection::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) @@ -6302,6 +6970,12 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; #endif } return NULL; @@ -6319,6 +6993,8 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO return ( mach_o::relocatable::Parser::validFile(fileContent) ); case CPU_TYPE_ARM: return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); + case CPU_TYPE_ARM64: + return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); } return false; } @@ -6344,6 +7020,11 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *subResult = header->cpusubtype(); return true; } + if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + *result = CPU_TYPE_ARM64; + *subResult = CPU_SUBTYPE_ARM64_ALL; + return true; + } return false; } @@ -6378,6 +7059,11 @@ bool hasObjC2Categories(const uint8_t* fileContent) else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); } +#if SUPPORT_ARCH_arm64 + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); + } +#endif return false; } diff --git a/src/ld/parsers/macho_relocatable_file.h b/src/ld/parsers/macho_relocatable_file.h index 021c3f2..a576f52 100644 --- a/src/ld/parsers/macho_relocatable_file.h +++ b/src/ld/parsers/macho_relocatable_file.h @@ -35,7 +35,9 @@ struct ParserOptions { uint32_t architecture; bool objSubtypeMustMatch; bool logAllFiles; - bool convertUnwindInfo; + bool warnUnwindConversionProblems; + bool keepDwarfUnwind; + bool forceDwarfConversion; uint32_t subType; }; diff --git a/src/ld/passes/compact_unwind.cpp b/src/ld/passes/compact_unwind.cpp index f86aee0..8b4a6bf 100644 --- a/src/ld/passes/compact_unwind.cpp +++ b/src/ld/passes/compact_unwind.cpp @@ -156,7 +156,6 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 std::vector lsdaIndex; makeLsdaIndex(uniqueEntries, lsdaIndex, lsdaIndexOffsetMap); - // calculate worst case size for all unwind info pages when allocating buffer const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); assert(uniqueEntries.size() > 0); @@ -294,6 +293,12 @@ bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc return ((enc & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); } +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF); +} + template void UnwindInfoAtom::compressDuplicates(const std::vector& entries, std::vector& uniqueEntries) { @@ -400,6 +405,14 @@ void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, co _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + template <> void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) { @@ -414,6 +427,12 @@ void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const l _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} template <> void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) @@ -429,6 +448,13 @@ void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::A _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + template <> void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) { @@ -443,6 +469,13 @@ void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld: _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + template <> void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) { @@ -457,6 +490,13 @@ void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + template <> void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) { @@ -473,6 +513,13 @@ void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, cons _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} @@ -552,6 +599,7 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); if ( encodingIndex <= 255 ) { pageSpecificEncodings[encoding] = encodingIndex; + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): pageSpecificEncodings[%d]=0x%08X\n", encodingIndex, encoding); } else { canDo = false; // case 3) @@ -685,7 +733,7 @@ static void getAllUnwindInfos(const ld::Internal& state, std::vectorbeginUnwind() == atom->endUnwind() ) { // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info - if ( atom->section().type() == ld::Section::typeCode ) { + if ( (atom->section().type() == ld::Section::typeCode) && (atom->size() !=0) ) { entries.push_back(UnwindEntry(atom, address, 0, NULL, NULL, NULL, 0)); } } @@ -781,6 +829,11 @@ static void makeFinalLinkedImageCompactUnwindSection(const Options& opts, ld::In case CPU_TYPE_I386: state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); + break; #endif default: assert(0 && "no compact unwind for arch"); @@ -830,13 +883,17 @@ template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup:: template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian32; template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64; template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; +#if SUPPORT_ARCH_arm64 +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64; +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; +#endif template CompactUnwindAtom::CompactUnwindAtom(ld::Internal& state,const ld::Atom* funcAtom, uint32_t startOffset, uint32_t len, uint32_t cui) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(log2(sizeof(pint_t)))), _atom(funcAtom), _startOffset(startOffset), _len(len), _compactUnwindInfo(cui) { _fixups.push_back(ld::Fixup(macho_compact_unwind_entry

::codeStartFieldOffset(), ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, funcAtom)); @@ -885,6 +942,11 @@ static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, cons case CPU_TYPE_I386: state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); + break; #endif } } diff --git a/src/ld/passes/dtrace_dof.cpp b/src/ld/passes/dtrace_dof.cpp index 74328ff..6f8a544 100644 --- a/src/ld/passes/dtrace_dof.cpp +++ b/src/ld/passes/dtrace_dof.cpp @@ -34,6 +34,7 @@ #include #include "ld.hpp" +#include "MachOFileAbstraction.hpp" #include "dtrace_dof.h" // prototype for entry point in libdtrace.dylib @@ -122,6 +123,10 @@ void doPass(const Options& opts, ld::Internal& internal) // only make __dof section in final linked images if ( opts.outputKind() == Options::kObjectFile ) return; + + // skip making __dof section if command line option said not to + if ( ! opts.generateDtraceDOF() ) + return; // scan all atoms looking for dtrace probes std::vector probeSites; @@ -138,11 +143,13 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindDtraceExtra: @@ -164,6 +171,7 @@ void doPass(const Options& opts, ld::Internal& internal) case CPU_TYPE_I386: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: storeKind = ld::Fixup::kindStoreLittleEndian32; break; default: diff --git a/src/ld/passes/got.cpp b/src/ld/passes/got.cpp index 3e6824f..2b9da3a 100644 --- a/src/ld/passes/got.cpp +++ b/src/ld/passes/got.cpp @@ -31,8 +31,10 @@ #include #include +#include "MachOFileAbstraction.hpp" #include "ld.hpp" #include "got.h" +#include "configure.h" namespace ld { namespace passes { @@ -42,17 +44,18 @@ class File; // forward reference class GOTEntryAtom : public ld::Atom { public: - GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport) + GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool is64) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), - _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), - _target(target) + symbolTableNotIn, false, false, false, (is64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2))), + _fixup(0, ld::Fixup::k1of1, (is64 ? ld::Fixup::kindStoreTargetAddressLittleEndian64 : ld::Fixup::kindStoreTargetAddressLittleEndian32), target), + _target(target), + _is64(is64) { _fixup.weakImport = weakImport; internal.addAtom(*this); } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _target->name(); } - virtual uint64_t size() const { return 8; } + virtual uint64_t size() const { return (_is64 ? 8 : 4); } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const { } virtual void setScope(Scope) { } @@ -62,6 +65,7 @@ public: private: mutable ld::Fixup _fixup; const ld::Atom* _target; + bool _is64; static ld::Section _s_section; }; @@ -73,6 +77,10 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom { switch (fixup->kind) { case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: +#endif // start by assuming this can be optimized *optimizable = true; // cannot do LEA optimization if target is in another dylib @@ -122,6 +130,9 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom } return true; case ld::Fixup::kindStoreX86PCRel32GOT: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64PCRelToGOT: +#endif *optimizable = false; return true; case ld::Fixup::kindNoneGroupSubordinatePersonality: @@ -189,7 +200,22 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::bindingDirectlyBound: fit->binding = ld::Fixup::bindingDirectlyBound; fit->u.target = targetOfGOT; - fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA; + switch ( fit->kind ) { + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + fit->kind = ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA; + break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21; + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + fit->kind = ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12; + break; +#endif + default: + assert(0 && "unsupported GOT reference kind"); + break; + } break; default: assert(0 && "unsupported GOT reference"); @@ -232,9 +258,33 @@ void doPass(const Options& opts, ld::Internal& internal) } } + bool is64 = false; + switch ( opts.architecture() ) { +#if SUPPORT_ARCH_i386 + case CPU_TYPE_I386: + is64 = false; + break; +#endif +#if SUPPORT_ARCH_x86_64 + case CPU_TYPE_X86_64: + is64 = true; + break; +#endif +#if SUPPORT_ARCH_arm_any + case CPU_TYPE_ARM: + is64 = false; + break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + is64 = true; + break; +#endif + } + // make GOT entries for (std::map::iterator it = gotMap.begin(); it != gotMap.end(); ++it) { - it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first]); + it->second = new GOTEntryAtom(internal, it->first, weakImportMap[it->first], is64); } // update atoms to use GOT entries diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index d471d98..d921a64 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -54,6 +54,7 @@ struct objc_image_info { #define OBJC_IMAGE_REQUIRES_GC (1<<2) #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) #define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) +#define OBJC_IMAGE_IS_SIMULATED (1<<5) @@ -111,6 +112,9 @@ ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, if ( compaction ) value |= OBJC_IMAGE_SUPPORTS_COMPACTION; break; + case ld::File::objcConstraintRetainReleaseForSimulator: + value |= OBJC_IMAGE_IS_SIMULATED; + break; } _content.version = 0; @@ -1176,10 +1180,18 @@ void doPass(const Options& opts, ld::Internal& state) opts.objCABIVersion2POverride() ? true : false)); break; #endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, true)); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + true)); + break; +#endif default: assert(0 && "unknown objc arch"); } @@ -1195,16 +1207,19 @@ void doPass(const Options& opts, ld::Internal& state) #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - // disable optimization until fully tested if ( opts.objCABIVersion2POverride() ) OptimizeCategories::doit(opts, state); break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - // disable optimization until fully tested OptimizeCategories::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + // disabled until tested + break; #endif default: assert(0 && "unknown objc arch"); diff --git a/src/ld/passes/order.cpp b/src/ld/passes/order.cpp index b4be79f..af73d66 100644 --- a/src/ld/passes/order.cpp +++ b/src/ld/passes/order.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include "ld.hpp" @@ -122,7 +123,7 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) { if ( left == right ) return false; - + // magic section$start symbol always sorts to the start of its section if ( left->contentType() == ld::Atom::typeSectionStart ) return true; @@ -162,6 +163,32 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) if ( right->contentType() == ld::Atom::typeSectionEnd ) return true; + // aliases sort before their target + bool leftIsAlias = left->isAlias(); + if ( leftIsAlias ) { + for (ld::Fixup::iterator fit=left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target == right ) + return true; // left already before right + left = fit->u.target; // sort as if alias was its target + break; + } + } + } + bool rightIsAlias = right->isAlias(); + if ( rightIsAlias ) { + for (ld::Fixup::iterator fit=right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target == left ) + return false; // need to swap, alias is after target + right = fit->u.target; // continue with sort as if right was target + break; + } + } + } + // the __common section can have real or tentative definitions // we want the real ones to sort before tentative ones bool leftIsTent = (left->definition() == ld::Atom::definitionTentative); @@ -204,8 +231,6 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) int64_t addrDiff = left->objectAddress() - right->objectAddress(); if ( addrDiff == 0 ) { // have same address so one might be an alias, and aliases need to sort before target - bool leftIsAlias = left->isAlias(); - bool rightIsAlias = right->isAlias(); if ( leftIsAlias != rightIsAlias ) return leftIsAlias; @@ -539,6 +564,7 @@ void Layout::doPass() // sort atoms in each section for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; + //fprintf(stderr, "sorting section %s\n", sect->sectionName()); std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); } @@ -547,7 +573,7 @@ void Layout::doPass() // ld::Internal::FinalSection* sect = *sit; // for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { // const ld::Atom* atom = *ait; - // fprintf(stderr, "\t%s\t%s\n", sect->sectionName(), atom->name()); + // fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); // } //} diff --git a/src/ld/passes/stubs/stub_arm64.hpp b/src/ld/passes/stubs/stub_arm64.hpp new file mode 100644 index 0000000..cb1ceb3 --- /dev/null +++ b/src/ld/passes/stubs/stub_arm64.hpp @@ -0,0 +1,390 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace arm64 { + + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return "image cache pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + + + + +// +// The stub-helper-helper is the common code factored out of each helper function. +// It is in the same section as the stub-helpers. +// Similar to the PLT0 entry in ELF. +// +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedImageCache(pass)), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedImageCache(pass)), + _fixup3(12, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedFastBinder(pass)), + _fixup4(16, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedFastBinder(pass)) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual const char* name() const { return "helper helper"; } + virtual uint64_t size() const { return 24; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0x90000011); // ADRP X17, dyld_mageLoaderCache@page + OSWriteLittleInt32(&buffer[ 4], 0, 0x91000231); // ADD X17, X17, dyld_mageLoaderCache@pageoff + OSWriteLittleInt32(&buffer[ 8], 0, 0xA9BF47F0); // STP X16/X17, [SP, #-16]! + OSWriteLittleInt32(&buffer[12], 0, 0x90000010); // ADRP X16, _fast_lazy_bind@page + OSWriteLittleInt32(&buffer[16], 0, 0xF9400210); // LDR X16, [X16,_fast_lazy_bind@pageoff] + OSWriteLittleInt32(&buffer[20], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup4)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, helperHelper(pass)), + _fixup2(8, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup3(8, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x18000050); // LDR W16, L0 + OSWriteLittleInt32(&buffer[4], 0, 0x14000000); // B helperhelper + OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // L0: .long 0 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(24, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, &stubTo), + _fixup2(28, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, lazyPointer), + _fixup3(32, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, lazyPointer) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 68; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xa9bf7bfd); // stp fp, lr, [sp, #-16]! + OSWriteLittleInt32(&buffer[ 4], 0, 0x910003fd); // mov fp, sp + OSWriteLittleInt32(&buffer[ 8], 0, 0xa9bf03e1); // stp x1, x0, [sp, #-16]! + OSWriteLittleInt32(&buffer[12], 0, 0xa9bf0be3); // stp x3, x2, [sp, #-16]! + OSWriteLittleInt32(&buffer[16], 0, 0xa9bf13e5); // stp x5, x4, [sp, #-16]! + OSWriteLittleInt32(&buffer[20], 0, 0xa9bf1be7); // stp x7, x6, [sp, #-16]! + OSWriteLittleInt32(&buffer[24], 0, 0x94000000); // bl _foo + OSWriteLittleInt32(&buffer[28], 0, 0x90000010); // adrp x16, lazy_pointer@PAGE + OSWriteLittleInt32(&buffer[32], 0, 0x91000210); // add x16, x16, lazy_pointer@PAGEOFF + OSWriteLittleInt32(&buffer[36], 0, 0xf9000200); // str x0, [x16] + OSWriteLittleInt32(&buffer[40], 0, 0xaa0003f0); // mov x16, x0 + OSWriteLittleInt32(&buffer[44], 0, 0xa8c11be7); // ldp x7, x6, [sp], #16 + OSWriteLittleInt32(&buffer[48], 0, 0xa8c113e5); // ldp x5, x4, [sp], #16 + OSWriteLittleInt32(&buffer[52], 0, 0xa8c10be3); // ldp x3, x2, [sp], #16 + OSWriteLittleInt32(&buffer[56], 0, 0xa8c103e1); // ldp x1, x0, [sp], #16 + OSWriteLittleInt32(&buffer[60], 0, 0xa8c17bfd); // ldp fp, lr, [sp], #16 + OSWriteLittleInt32(&buffer[64], 0, 0xd61f0200); // br x16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _helper(pass, this, stubTo), + _resolverHelper(pass, this, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + stubToResolver ? &_resolverHelper : + (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x90000010); // ADRP X16, lazy_pointer@page + OSWriteLittleInt32(&buffer[4], 0, 0xF9400210); // LDR X16, [X16, lazy_pointer@pageoff] + OSWriteLittleInt32(&buffer[8], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + + +class NonLazyPointerAtom : public ld::Atom { +public: + NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &stubTo) { + pass.addAtom(*this); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + + static ld::Section _s_section; +}; + +ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +class KextStubAtom : public ld::Atom { +public: + KextStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _nonLazyPointer(pass, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_nonLazyPointer), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_nonLazyPointer) { + pass.addAtom(*this); + asprintf((char**)&_name, "%s.stub", _stubTo.name()); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x90000010); // ADRP X16, non_lazy_pointer@page + OSWriteLittleInt32(&buffer[4], 0, 0xF9400210); // LDR X16, [X16, non_lazy_pointer@pageoff] + OSWriteLittleInt32(&buffer[8], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + const char* _name; + NonLazyPointerAtom _nonLazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeCode); + + +} // namespace x86_64 + diff --git a/src/ld/passes/stubs/stubs.cpp b/src/ld/passes/stubs/stubs.cpp index 5ea1b05..c01f3f9 100644 --- a/src/ld/passes/stubs/stubs.cpp +++ b/src/ld/passes/stubs/stubs.cpp @@ -35,8 +35,8 @@ #include #include "Options.h" -#include "ld.hpp" #include "MachOFileAbstraction.hpp" +#include "ld.hpp" #include "make_stubs.h" @@ -90,8 +90,9 @@ private: #include "stub_x86_classic.hpp" #include "stub_arm.hpp" #include "stub_arm_classic.hpp" - - +#if SUPPORT_ARCH_arm64 +#include "stub_arm64.hpp" +#endif Pass::Pass(const Options& opts) : compressedHelperHelper(NULL), @@ -119,6 +120,9 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif assert(target != NULL); // create stub if target is in a dylib if ( target->definition() == ld::Atom::definitionProxy ) @@ -216,6 +220,14 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) return new ld::passes::stubs::arm::classic::StubNoPICAtom(*this, target, forLazyDylib, weakImport); } break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) + return new ld::passes::stubs::arm64::KextStubAtom(*this, target); + else + return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + break; #endif } throw "unsupported arch for stub"; diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index cc673a3..36fcce9 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -804,6 +804,39 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbHigh16: printf(", then store high-16 in Thumb movt"); break; + case ld::Fixup::kindStoreARM64Branch26: + printf(", then store as ARM64 26-bit pcrel branch"); + break; + case ld::Fixup::kindStoreARM64Page21: + printf(", then store as ARM64 21-bit pcrel ADRP"); + break; + case ld::Fixup::kindStoreARM64PageOff12: + printf(", then store as ARM64 12-bit offset"); + break; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of GOT"); + break; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + printf(", then store as ARM64 12-bit page offset of GOT"); + break; + case ld::Fixup::kindStoreARM64GOTLeaPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of GOT lea"); + break; + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + printf(", then store as ARM64 12-bit page offset of GOT lea"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of TLVP"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + printf(", then store as ARM64 12-bit page offset of TLVP"); + break; + case ld::Fixup::kindStoreARM64PointerToGOT: + printf(", then store as 64-bit pointer to GOT entry"); + break; + case ld::Fixup::kindStoreARM64PCRelToGOT: + printf(", then store as 32-bit delta to GOT entry"); + break; case ld::Fixup::kindDtraceExtra: printf("dtrace static probe extra info"); break; @@ -825,6 +858,12 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: printf("Thumb dtrace static is-enabled site"); break; + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + printf("ARM64 dtrace static probe site"); + break; + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + printf("ARM64 dtrace static is-enabled site"); + break; case ld::Fixup::kindLazyTarget: printf("lazy reference to external symbol %s", referenceTargetAtomName(ref)); break; @@ -898,6 +937,28 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: printf("tlv template offset of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + printf("ARM64 store 26-bit pcrel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64Page21: + printf("ARM64 store 21-bit pcrel ADRP to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + printf("ARM64 store 12-bit page offset of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + printf("ARM64 store 21-bit pcrel ADRP to GOT for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + printf("ARM64 store 12-bit page offset of GOT of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + printf("ARM64 store 21-bit pcrel ADRP for lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + printf("ARM64 store 12-bit page offset of lea of %s", referenceTargetAtomName(ref)); + break; //default: // printf("unknown fixup"); // break; @@ -1116,7 +1177,9 @@ static ld::relocatable::File* createReader(const char* path) objOpts.architecture = sPreferredArch; objOpts.objSubtypeMustMatch = false; objOpts.logAllFiles = false; - objOpts.convertUnwindInfo = true; + objOpts.warnUnwindConversionProblems = true; + objOpts.keepDwarfUnwind = false; + objOpts.forceDwarfConversion = false; objOpts.subType = sPreferredSubArch; #if 1 if ( ! foundFatSlice ) { diff --git a/src/other/PruneTrie.cpp b/src/other/PruneTrie.cpp index 766ae94..e47d482 100644 --- a/src/other/PruneTrie.cpp +++ b/src/other/PruneTrie.cpp @@ -98,3 +98,16 @@ prune_trie( // success return NULL; } + + +// Switch libprunetrie to use libc++ instead of libstdc++ +// The one undefined when building libprunetrie.a with libc++ is throw_length_error(). +// Adding this define here resolves that and means libprunetrie.a can be linked +// by cctools with libc++ or libstdc++. +extern "C" void foobar() __asm("__ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv"); +void foobar() +{ + throw "Size of vecor cannot be grown"; +} + + diff --git a/src/other/dyldinfo.cpp b/src/other/dyldinfo.cpp index 71f13b8..b43ff3f 100644 --- a/src/other/dyldinfo.cpp +++ b/src/other/dyldinfo.cpp @@ -252,6 +252,26 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) } #endif +#if SUPPORT_ARCH_arm64 +template <> +bool DyldInfoPrinter::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} +#endif + template DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch) : fHeader(NULL), fLength(fileLength), @@ -612,7 +632,7 @@ void DyldInfoPrinter::printRebaseInfo() uint64_t segOffset = 0; uint32_t count; uint32_t skip; - int segIndex; + int segIndex = 0; pint_t segStartAddr = 0; const char* segName = "??"; const char* typeName = "??"; @@ -1267,13 +1287,14 @@ void DyldInfoPrinter::printExportInfo() const bool reExport = (it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT); const bool weakDef = (it->flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); const bool threadLocal = ((it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL); + const bool abs = ((it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE); const bool resolver = (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER); if ( reExport ) printf("[re-export] "); else printf("0x%08llX ", fBaseAddress+it->address); printf("%s", it->name); - if ( weakDef || threadLocal || resolver ) { + if ( weakDef || threadLocal || resolver || abs ) { bool needComma = false; printf(" ["); if ( weakDef ) { @@ -1286,6 +1307,12 @@ void DyldInfoPrinter::printExportInfo() printf("per-thread"); needComma = true; } + if ( abs ) { + if ( needComma ) + printf(", "); + printf("absolute"); + needComma = true; + } if ( resolver ) { if ( needComma ) printf(", "); @@ -1432,6 +1459,8 @@ void DyldInfoPrinter::printExportInfoNodes() printf("[addr=0x%06llX] ", address); else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL) printf("[flags=THREAD_LOCAL addr=0x%06llX] ", address); + else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE) + printf("[flags=ABSOLUTE addr=0x%06llX] ", address); else printf("[flags=0x%llX addr=0x%06llX] ", flags, address); } @@ -1467,7 +1496,12 @@ const uint8_t* DyldInfoPrinter::printSharedRegionInfoForEachULEB128Address(co kindStr = "64-bit pointer"; break; case 3: - kindStr = "ppc hi16"; +#if SUPPORT_ARCH_arm64 + if ( fHeader->cputype() == CPU_TYPE_ARM64 ) + kindStr = "arm64 ADRP"; + else +#endif + kindStr = "ppc hi16"; break; case 4: kindStr = "32-bit offset to IMPORT"; @@ -1751,7 +1785,6 @@ x86::P::uint_t DyldInfoPrinter::relocBase() template <> x86_64::P::uint_t DyldInfoPrinter::relocBase() { - // check for split-seg return fFirstWritableSegment->vmaddr(); } @@ -1766,6 +1799,13 @@ arm::P::uint_t DyldInfoPrinter::relocBase() } #endif +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t DyldInfoPrinter::relocBase() +{ + return fFirstWritableSegment->vmaddr(); +} +#endif template <> const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) @@ -1818,6 +1858,16 @@ const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) } #endif +#if SUPPORT_ARCH_arm64 +template <> +const char* DyldInfoPrinter::relocTypeName(uint8_t r_type) +{ + if ( r_type == ARM64_RELOC_UNSIGNED ) + return "pointer"; + return "??"; +} +#endif + template void DyldInfoPrinter::printRelocRebaseInfo() { @@ -2114,6 +2164,14 @@ static void dump(const char* path) else throw "in universal file, arm slice does not contain arm mach-o"; break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( DyldInfoPrinter::validFile(p + offset) ) + DyldInfoPrinter::make(p + offset, size, path, (sPreferredArch == 0)); + else + throw "in universal file, arm64 slice does not contain arm mach-o"; + break; #endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); @@ -2137,6 +2195,11 @@ static void dump(const char* path) else if ( DyldInfoPrinter::validFile(p) ) { DyldInfoPrinter::make(p, length, path, false); } +#endif +#if SUPPORT_ARCH_arm64 + else if ( DyldInfoPrinter::validFile(p) ) { + DyldInfoPrinter::make(p, length, path, false); + } #endif else { throw "not a known file type"; @@ -2187,6 +2250,10 @@ int main(int argc, const char* argv[]) sPreferredArch = CPU_TYPE_I386; else if ( strcmp(arch, "x86_64") == 0 ) sPreferredArch = CPU_TYPE_X86_64; +#if SUPPORT_ARCH_arm64 + else if ( strcmp(arch, "arm64") == 0 ) + sPreferredArch = CPU_TYPE_ARM64; +#endif else { if ( arch == NULL ) throw "-arch missing architecture name"; diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index bd585d1..aec6ebe 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -264,11 +264,34 @@ bool MachOChecker::validFile(const uint8_t* fileContent) return false; } +#if SUPPORT_ARCH_arm64 +template <> +bool MachOChecker::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + return true; + } + return false; +} +#endif + template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x03; } +#if SUPPORT_ARCH_arm64 +template <> uint8_t MachOChecker::loadCommandSizeMask() { return 0x07; } +#endif template <> @@ -301,9 +324,13 @@ arm::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_comm return threadInfo->thread_register(13); } - - - +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::getInitialStackPointer(const macho_thread_command* threadInfo) +{ + throw "LC_UNIXTHREAD not supported for arm64"; +} +#endif template <> ppc::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) @@ -335,6 +362,13 @@ arm::P::uint_t MachOChecker::getEntryPoint(const macho_thread_commandthread_register(15); } +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::getEntryPoint(const macho_thread_command* threadInfo) +{ + throw "LC_UNIXTHREAD not supported for arm64"; +} +#endif template MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) @@ -447,6 +481,7 @@ void MachOChecker::checkLoadCommands() fDyldInfo = (macho_dyld_info_command

*)cmd; break; case LC_ENCRYPTION_INFO: + case LC_ENCRYPTION_INFO_64: encryption_info = (macho_encryption_info_command

*)cmd; break; case LC_SUB_UMBRELLA: @@ -993,6 +1028,15 @@ arm::P::uint_t MachOChecker::relocBase() return fFirstSegment->vmaddr(); } +#if SUPPORT_ARCH_arm64 +template <> +arm64::P::uint_t MachOChecker::relocBase() +{ + return fFirstWritableSegment->vmaddr(); +} +#endif + + template bool MachOChecker::addressInWritableSegment(pint_t address) @@ -1107,6 +1151,14 @@ void MachOChecker::checkExternalReloation(const macho_relocation_info

* r } #endif +#if SUPPORT_ARCH_arm64 +template <> +void MachOChecker::checkExternalReloation(const macho_relocation_info

* reloc) +{ + throw "external relocations not used for arm64"; +} +#endif + template <> void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) @@ -1184,6 +1236,15 @@ void MachOChecker::checkLocalReloation(const macho_relocation_info

* relo } #endif +#if SUPPORT_ARCH_arm64 +template <> +void MachOChecker::checkLocalReloation(const macho_relocation_info

* reloc) +{ + throw "local relocations not used for arm64"; +} +#endif + + template void MachOChecker::checkRelocations() { @@ -1602,6 +1663,11 @@ static void check(const char* path) else if ( MachOChecker::validFile(p) ) { MachOChecker::make(p, length, path); } +#endif +#if SUPPORT_ARCH_arm64 + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } #endif else { throw "not a known file type"; diff --git a/src/other/unwinddump.cpp b/src/other/unwinddump.cpp index 731f2a3..3da01c5 100644 --- a/src/other/unwinddump.cpp +++ b/src/other/unwinddump.cpp @@ -35,7 +35,7 @@ #include #include - +#include "configure.h" #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -99,7 +99,9 @@ private: template <> const char* UnwindPrinter::archName() { return "i386"; } template <> const char* UnwindPrinter::archName() { return "x86_64"; } template <> const char* UnwindPrinter::archName() { return "arm"; } - +#if SUPPORT_ARCH_arm64 +template <> const char* UnwindPrinter::archName() { return "arm64"; } +#endif template <> bool UnwindPrinter::validFile(const uint8_t* fileContent) @@ -140,6 +142,27 @@ bool UnwindPrinter::validFile(const uint8_t* fileContent) } +#if SUPPORT_ARCH_arm64 +template <> +bool UnwindPrinter::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return false; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return false; + switch (header->filetype()) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DYLINKER: + case MH_OBJECT: + return true; + } + return false; +} +#endif + template UnwindPrinter::UnwindPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool showFunctionNames) : fHeader(NULL), fLength(fileLength), fUnwindSection(NULL), @@ -628,7 +651,84 @@ void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, cha } - +#if SUPPORT_ARCH_arm64 +template <> +void UnwindPrinter::decode(uint32_t encoding, const uint8_t* funcStart, char* str) +{ + uint32_t stackSize; + switch ( encoding & UNWIND_ARM64_MODE_MASK ) { + case UNWIND_ARM64_MODE_FRAMELESS: + stackSize = EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); + if ( stackSize == 0 ) + strcpy(str, "no frame, no saved registers "); + else + sprintf(str, "stack size=%d: ", 16 * stackSize); + if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR ) + strcat(str, "x19/20 "); + if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR ) + strcat(str, "x21/22 "); + if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR ) + strcat(str, "x23/24 "); + if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR ) + strcat(str, "x25/26 "); + if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR ) + strcat(str, "x27/28 "); + if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR ) + strcat(str, "d8/9 "); + if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR ) + strcat(str, "d10/11 "); + if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR ) + strcat(str, "d12/13 "); + if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR ) + strcat(str, "d14/15 "); + break; + break; + case UNWIND_ARM64_MODE_DWARF: + sprintf(str, "dwarf offset 0x%08X, ", encoding & UNWIND_X86_64_DWARF_SECTION_OFFSET); + break; + case UNWIND_ARM64_MODE_FRAME: + strcpy(str, "std frame: "); + if ( encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR ) + strcat(str, "x19/20 "); + if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR ) + strcat(str, "x21/22 "); + if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR ) + strcat(str, "x23/24 "); + if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR ) + strcat(str, "x25/26 "); + if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR ) + strcat(str, "x27/28 "); + if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR ) + strcat(str, "d8/9 "); + if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR ) + strcat(str, "d10/11 "); + if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR ) + strcat(str, "d12/13 "); + if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR ) + strcat(str, "d14/15 "); + break; + case UNWIND_ARM64_MODE_FRAME_OLD: + strcpy(str, "old frame: "); + if ( encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD ) + strcat(str, "x21/22 "); + if ( encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD ) + strcat(str, "x23/24 "); + if ( encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD ) + strcat(str, "x25/26 "); + if ( encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD ) + strcat(str, "x27/28 "); + if ( encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD ) + strcat(str, "d8/9 "); + if ( encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD ) + strcat(str, "d10/11 "); + if ( encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD ) + strcat(str, "d12/13 "); + if ( encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD ) + strcat(str, "d14/15 "); + break; + } +} +#endif template <> const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) @@ -648,6 +748,17 @@ const char* UnwindPrinter::personalityName(const macho_relocation_info +const char* UnwindPrinter::personalityName(const macho_relocation_info* reloc) +{ + //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section"); + //assert((reloc->r_type() == ARM64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section"); + const macho_nlist

& sym = fSymbols[reloc->r_symbolnum()]; + return &fStrings[sym.n_strx()]; +} +#endif + template bool UnwindPrinter::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr) { @@ -715,7 +826,6 @@ void UnwindPrinter::printObjectUnwindSection(bool showFunctionNames) printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset); } } - } @@ -789,7 +899,7 @@ void UnwindPrinter::printUnwindSection(bool showFunctionNames) char encodingString[100]; decode(entry[j].encoding(), ((const uint8_t*)fHeader)+funcOffset, encodingString); const char* name = showFunctionNames ? functionName(funcOffset+fMachHeaderAddress) : ""; - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-40s) %s\n", + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding=0x%08X (%-56s) %s\n", j, funcOffset, entry[j].encoding(), encodingString, name); } } @@ -827,7 +937,7 @@ void UnwindPrinter::printUnwindSection(bool showFunctionNames) fprintf(stderr, "MISSING LSDA entry for %s\n", name); } } - printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-40s) %s\n", + printf("\t\t\t[%3u] funcOffset=0x%08X, encoding[%3u]=0x%08X (%-56s) %s\n", j, funcOff, encodingIndex, encoding, encodingString, name); } } @@ -875,6 +985,14 @@ static void dump(const char* path, const std::set& onlyArchs, bool s else throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; break; +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( UnwindPrinter::validFile(p + offset) ) + UnwindPrinter::make(p + offset, size, path, showFunctionNames); + else + throw "in universal file, arm64 slice does not contain arm mach-o"; + break; +#endif default: throwf("in universal file, unknown architecture slice 0x%x\n", cputype); } @@ -887,6 +1005,11 @@ static void dump(const char* path, const std::set& onlyArchs, bool s else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) { UnwindPrinter::make(p, length, path, showFunctionNames); } +#if SUPPORT_ARCH_arm64 + else if ( UnwindPrinter::validFile(p) && onlyArchs.count(CPU_TYPE_ARM64) ) { + UnwindPrinter::make(p, length, path, showFunctionNames); + } +#endif else { throw "not a known file type"; } @@ -913,6 +1036,10 @@ int main(int argc, const char* argv[]) onlyArchs.insert(CPU_TYPE_I386); else if ( strcmp(arch, "x86_64") == 0 ) onlyArchs.insert(CPU_TYPE_X86_64); +#if SUPPORT_ARCH_arm64 + else if ( strcmp(arch, "arm64") == 0 ) + onlyArchs.insert(CPU_TYPE_ARM64); +#endif else throwf("unknown architecture %s", arch); } @@ -932,6 +1059,9 @@ int main(int argc, const char* argv[]) if ( onlyArchs.size() == 0 ) { onlyArchs.insert(CPU_TYPE_I386); onlyArchs.insert(CPU_TYPE_X86_64); +#if SUPPORT_ARCH_arm64 + onlyArchs.insert(CPU_TYPE_ARM64); +#endif } // process each file diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 41cf6cf..fdb6212 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -125,6 +125,17 @@ else FILEARCH = $(ARCH) endif +ifeq ($(ARCH),arm64) + LDFLAGS := -syslibroot $(IOS_SDK) + CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + VERSION_NEW_LINKEDIT = -miphoneos-version-min=7.0 + VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 + LD_SYSROOT = -syslibroot $(IOS_SDK) + LD_NEW_LINKEDIT = -ios_version_min 7.0 +else + FILEARCH = $(ARCH) +endif RM = rm RMFLAGS = -rf diff --git a/unit-tests/test-cases/coalesce-force/Makefile b/unit-tests/test-cases/coalesce-force/Makefile new file mode 100644 index 0000000..92aadd7 --- /dev/null +++ b/unit-tests/test-cases/coalesce-force/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test -force_symbols_coalesce_list +# + + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${DYLDINFO} -weak_bind libfoo.dylib | grep _foo1 | ${FAIL_IF_STDIN} + ${DYLDINFO} -weak_bind libfoo.dylib | grep _wildcheck | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib -Wl,-force_symbols_coalesce_list,foo.exp + ${DYLDINFO} -weak_bind libfoo2.dylib | grep _foo1 | ${FAIL_IF_EMPTY} + ${DYLDINFO} -weak_bind libfoo2.dylib | grep _wildcheck | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libfoo2.dylib + + + +clean: + rm -rf libfoo.dylib libfoo2.dylib diff --git a/unit-tests/test-cases/coalesce-force/foo.c b/unit-tests/test-cases/coalesce-force/foo.c new file mode 100644 index 0000000..411d4c0 --- /dev/null +++ b/unit-tests/test-cases/coalesce-force/foo.c @@ -0,0 +1,21 @@ + + +void foo1() {} +void foo3() {} + + +__attribute__((weak)) void foo2() {} +__attribute__((weak)) void foo4() {} + + +void wildcheck() {} +void willnot() {} + + + +__attribute__((weak)) void patterncheck() {} +__attribute__((weak)) void patnot() {} + + +void* pointers[] = { &foo1, &foo2, &foo3, &foo4, &wildcheck, &willnot, &patterncheck, &patnot }; + diff --git a/unit-tests/test-cases/coalesce-force/foo.exp b/unit-tests/test-cases/coalesce-force/foo.exp new file mode 100644 index 0000000..7268896 --- /dev/null +++ b/unit-tests/test-cases/coalesce-force/foo.exp @@ -0,0 +1,2 @@ +_foo1 +_wild* diff --git a/unit-tests/test-cases/data-in-code/Makefile b/unit-tests/test-cases/data-in-code/Makefile index 0897726..b1e7fd5 100644 --- a/unit-tests/test-cases/data-in-code/Makefile +++ b/unit-tests/test-cases/data-in-code/Makefile @@ -29,9 +29,12 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} -c test.s -o test.o + ${CC} ${CCFLAGS} test.o -dynamiclib -o libtest.dylib + dyldinfo -arch ${ARCH} -data_in_code libtest.dylib| ${FAIL_IF_EMPTY} ${LD} -r -arch ${ARCH} test.o -o test2.o - #nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY} - #${PASS_IFF_GOOD_MACHO} main + ${CC} ${CCFLAGS} test2.o -dynamiclib -o libtest2.dylib + dyldinfo -arch ${ARCH} -data_in_code libtest2.dylib| ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib clean: - rm -rf test.o test2.o + rm -rf test.o test2.o libtest.dylib libtest2.dylib diff --git a/unit-tests/test-cases/data-in-code/main.c b/unit-tests/test-cases/data-in-code/main.c deleted file mode 100644 index 811449a..0000000 --- a/unit-tests/test-cases/data-in-code/main.c +++ /dev/null @@ -1,49 +0,0 @@ - -#include - -#define DTRACE_STRINGIFY(s) #s -#define DTRACE_TOSTRING(s) DTRACE_STRINGIFY(s) - -#define DTRACE_NOPS \ - "nop" "\n\t" \ - "nop" "\n\t" \ - "nop" "\n\t" - - -#define DTRACE_LAB(p, n) \ - "__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n) - -#define DTRACE_LABEL(p, n) \ - ".section __DATA, __data\n\t" \ - ".globl " DTRACE_LAB(p, n) "\n\t" \ - DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \ - ".text" "\n\t" \ - "1:" - -#define DTRACE_CALL(p,n) \ - DTRACE_LABEL(p,n) \ - DTRACE_NOPS - -#define DTRACE_CALL0ARGS(provider, name) \ - __asm volatile ( \ - DTRACE_CALL(provider, name) \ - : \ - : \ - ); - -int deadwood() -{ - DTRACE_CALL0ARGS(__foo__, test2) - return 0; -} - - -int main() { - int a = 1; - - while(a) { - DTRACE_CALL0ARGS(__foo__, test1) - } - - return 0; -} diff --git a/unit-tests/test-cases/data-in-code/test.s b/unit-tests/test-cases/data-in-code/test.s index 8dc92b8..19c2448 100644 --- a/unit-tests/test-cases/data-in-code/test.s +++ b/unit-tests/test-cases/data-in-code/test.s @@ -4,21 +4,23 @@ _foo: nop nop -l$start$data$1: + .data_region nop nop nop -l$start$jt8$2: + .data_region jt8 nop nop -l$start$jt16$3: +.data_region jt16 nop nop -l$start$code$n4: +.data_region jt32 nop nop - - +.end_data_region + nop + nop + .subsections_via_symbols diff --git a/unit-tests/test-cases/linker_options-framework/Makefile b/unit-tests/test-cases/linker_options-framework/Makefile new file mode 100644 index 0000000..3ac482e --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check linker options work for -framework +# + +run: all + +all: + mkdir -p Foo.framework + ${CC} ${CCFLAGS} foo.c -dynamiclib -o Foo.framework/Foo + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} -r main.o -add_linker_option '-framework Foo' -o main2.o + ${CC} ${CCFLAGS} main2.o -o main -F. + ${DYLDINFO} -lazy_bind main | grep _foo | grep Foo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main libfoo.dylib main.o main2.o Foo.framework + diff --git a/unit-tests/test-cases/linker_options-framework/foo.c b/unit-tests/test-cases/linker_options-framework/foo.c new file mode 100644 index 0000000..e704870 --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework/foo.c @@ -0,0 +1,3 @@ + + +void foo() { } diff --git a/unit-tests/test-cases/linker_options-framework/main.c b/unit-tests/test-cases/linker_options-framework/main.c new file mode 100644 index 0000000..4f56fe0 --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/unit-tests/test-cases/linker_options-library/Makefile b/unit-tests/test-cases/linker_options-library/Makefile new file mode 100644 index 0000000..93697e5 --- /dev/null +++ b/unit-tests/test-cases/linker_options-library/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check linker options work for -l +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static bar.o -o libbar.a + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} -r main.o -add_linker_option -lfoo -add_linker_option -lbar -o main2.o + ${CC} ${CCFLAGS} main2.o -o main -L. + ${DYLDINFO} -lazy_bind main | grep _foo | grep libfoo | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -f main libfoo.dylib main.o main2.o bar.o libbar.a + diff --git a/unit-tests/test-cases/linker_options-library/bar.c b/unit-tests/test-cases/linker_options-library/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/unit-tests/test-cases/linker_options-library/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/unit-tests/test-cases/linker_options-library/foo.c b/unit-tests/test-cases/linker_options-library/foo.c new file mode 100644 index 0000000..e704870 --- /dev/null +++ b/unit-tests/test-cases/linker_options-library/foo.c @@ -0,0 +1,3 @@ + + +void foo() { } diff --git a/unit-tests/test-cases/linker_options-library/main.c b/unit-tests/test-cases/linker_options-library/main.c new file mode 100644 index 0000000..594cf17 --- /dev/null +++ b/unit-tests/test-cases/linker_options-library/main.c @@ -0,0 +1,10 @@ + +extern void foo(); +extern void bar(); + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/unit-tests/test-cases/lto-dynamic_export/Makefile b/unit-tests/test-cases/lto-dynamic_export/Makefile new file mode 100644 index 0000000..416c5a9 --- /dev/null +++ b/unit-tests/test-cases/lto-dynamic_export/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify -preload -pie produces relocations +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto main.c -c -o main.o + ${CC} ${CCFLAGS} main.o -o main + nm -g main | grep _bar | ${FAIL_IF_STDIN} + ${CC} ${CCFLAGS} main.o -o main-de -Wl,-export_dynamic + nm -g main-de | grep _bar | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main-de + + +clean: + rm main.o main main-de diff --git a/unit-tests/test-cases/lto-dynamic_export/main.c b/unit-tests/test-cases/lto-dynamic_export/main.c new file mode 100644 index 0000000..bb3ced0 --- /dev/null +++ b/unit-tests/test-cases/lto-dynamic_export/main.c @@ -0,0 +1,14 @@ + +__attribute__((visibility("hidden"))) +void foo() { } + +void bar() { } + + +int main() +{ + foo(); + bar(); + + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/order_file-archive/Makefile b/unit-tests/test-cases/order_file-archive/Makefile new file mode 100644 index 0000000..eb77525 --- /dev/null +++ b/unit-tests/test-cases/order_file-archive/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify order files can pick file in archives +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o libfoo.a -Wl,-order_file,main.order -o main + ${FAIL_IF_BAD_MACHO} main + nm -n -j main | egrep "_main|_foo" > main.actual + ${PASS_IFF} diff main.actual main.expected + + +clean: + rm -rf *.o libfoo.a main main.actual diff --git a/unit-tests/test-cases/order_file-archive/foo.c b/unit-tests/test-cases/order_file-archive/foo.c new file mode 100644 index 0000000..61be976 --- /dev/null +++ b/unit-tests/test-cases/order_file-archive/foo.c @@ -0,0 +1,4 @@ +void foo1() { } +void foo2() {} +void foo3() {} + diff --git a/unit-tests/test-cases/order_file-archive/main.c b/unit-tests/test-cases/order_file-archive/main.c new file mode 100644 index 0000000..298aedc --- /dev/null +++ b/unit-tests/test-cases/order_file-archive/main.c @@ -0,0 +1,33 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern void foo1(); + +int main() +{ + foo1(); + return 0; +} diff --git a/unit-tests/test-cases/order_file-archive/main.expected b/unit-tests/test-cases/order_file-archive/main.expected new file mode 100644 index 0000000..31a7e2f --- /dev/null +++ b/unit-tests/test-cases/order_file-archive/main.expected @@ -0,0 +1,4 @@ +_foo2 +_main +_foo3 +_foo1 diff --git a/unit-tests/test-cases/order_file-archive/main.order b/unit-tests/test-cases/order_file-archive/main.order new file mode 100644 index 0000000..195b1d7 --- /dev/null +++ b/unit-tests/test-cases/order_file-archive/main.order @@ -0,0 +1,5 @@ +_foo2 +_main +libfoo.a(foo.o):_foo3 +_foo1 + diff --git a/unit-tests/test-cases/re-export-symbol/Makefile b/unit-tests/test-cases/re-export-symbol/Makefile index 4da677c..2ad9e0c 100644 --- a/unit-tests/test-cases/re-export-symbol/Makefile +++ b/unit-tests/test-cases/re-export-symbol/Makefile @@ -44,10 +44,12 @@ all: ${DYLDINFO} -bind -lazy_bind main1 | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} ${DYLDINFO} -bind -lazy_bind main1 | grep _bar_weak | grep libfoo | ${FAIL_IF_EMPTY} - # build library the re-exports _bar from base library as _mybar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp + # build library that re-exports _bar from base library as _mybar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp -DUSE_MY ${FAIL_IF_BAD_MACHO} libfoo2.dylib ${DYLDINFO} -export libfoo2.dylib | grep _mybar | grep 're-export' | grep _bar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind libfoo2.dylib | grep _bar | grep libbar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind libfoo2.dylib | grep _bar | grep libbar | ${FAIL_IF_EMPTY} # link against dylib and verify _mybar is marked as coming from libfoo ${CC} ${CCFLAGS} main2.c libfoo2.dylib -o main2 ${DYLDINFO} -bind -lazy_bind main2 | grep _mybar | grep libfoo2 | ${FAIL_IF_EMPTY} diff --git a/unit-tests/test-cases/re-export-symbol/foo.c b/unit-tests/test-cases/re-export-symbol/foo.c index 714540a..61981fe 100644 --- a/unit-tests/test-cases/re-export-symbol/foo.c +++ b/unit-tests/test-cases/re-export-symbol/foo.c @@ -1,4 +1,21 @@ + +#if USE_MY + extern int mybar(); +#else + extern int bar(); +#endif + int foo(void) { - return 1; +#if USE_MY + return mybar() + 1; +#else + return bar() + 1; +#endif } + +#if USE_MY + void* p = &mybar; +#else + void* p = &bar; +#endif -- 2.45.2