From 4096c83d62922a335203224cd21fb4402153bdf3 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Thu, 14 May 2015 00:23:48 +0100 Subject: [PATCH] 236-3 --- ld64/doc/man/man1/ld.1 | 14 +- ld64/ld64.xcodeproj/project.pbxproj | 37 +- ld64/src/abstraction/MachOFileAbstraction.hpp | 273 ++- ld64/src/abstraction/MachOTrie.hpp | 2 +- ld64/src/create_configure | 19 +- ld64/src/ld/Architectures.hpp | 5 + ld64/src/ld/HeaderAndLoadCommands.hpp | 136 +- ld64/src/ld/InputFiles.cpp | 175 +- ld64/src/ld/InputFiles.h | 12 +- ld64/src/ld/LinkEdit.hpp | 112 +- ld64/src/ld/LinkEditClassic.hpp | 206 +++ ld64/src/ld/Options.cpp | 481 ++++- ld64/src/ld/Options.h | 70 +- ld64/src/ld/OutputFile.cpp | 1858 ++++++++++++++++---- ld64/src/ld/OutputFile.h | 34 +- ld64/src/ld/Resolver.cpp | 154 +- ld64/src/ld/Resolver.h | 8 +- ld64/src/ld/SymbolTable.cpp | 26 + ld64/src/ld/SymbolTable.h | 1 + ld64/src/ld/dwarf2.h | 8 +- ld64/src/ld/ld.cpp | 339 +++- ld64/src/ld/ld.hpp | 155 +- ld64/src/ld/parsers/archive_file.cpp | 15 +- .../src/ld/parsers/libunwind/DwarfInstructions.hpp | 308 +++- ld64/src/ld/parsers/libunwind/DwarfParser.hpp | 2 +- ld64/src/ld/parsers/libunwind/Registers.hpp | 281 ++- ld64/src/ld/parsers/lto_file.cpp | 83 +- ld64/src/ld/parsers/lto_file.h | 7 +- ld64/src/ld/parsers/macho_dylib_file.cpp | 220 ++- ld64/src/ld/parsers/macho_dylib_file.h | 6 + ld64/src/ld/parsers/macho_relocatable_file.cpp | 1112 ++++++++++-- ld64/src/ld/parsers/macho_relocatable_file.h | 6 +- ld64/src/ld/passes/branch_island.cpp | 382 ++-- ld64/src/ld/passes/compact_unwind.cpp | 76 +- ld64/src/ld/passes/dtrace_dof.cpp | 8 + ld64/src/ld/passes/got.cpp | 64 +- ld64/src/ld/passes/objc.cpp | 19 +- ld64/src/ld/passes/order.cpp | 68 +- ld64/src/ld/passes/stubs/stub_arm64.hpp | 396 +++++ ld64/src/ld/passes/stubs/stubs.cpp | 18 +- ld64/src/ld/passes/tlvp.cpp | 12 + ld64/src/other/ObjectDump.cpp | 117 +- ld64/src/other/dyldinfo.cpp | 77 +- ld64/src/other/machochecker.cpp | 72 +- ld64/src/other/unwinddump.cpp | 142 +- ld64/unit-tests/include/common.makefile | 19 +- ld64/unit-tests/test-cases/branch-islands/Makefile | 4 +- ld64/unit-tests/test-cases/branch-islands/extra.c | 2 + ld64/unit-tests/test-cases/branch-islands/hello.c | 5 +- ld64/unit-tests/test-cases/branch-islands/space.s | 6 +- ld64/unit-tests/test-cases/branch-long/Makefile | 59 + ld64/unit-tests/test-cases/branch-long/bar.s | 24 + ld64/unit-tests/test-cases/branch-long/foo.c | 25 + .../{eh-coalescing-r => coalesce-force}/Makefile | 36 +- ld64/unit-tests/test-cases/coalesce-force/foo.c | 21 + ld64/unit-tests/test-cases/coalesce-force/foo.exp | 2 + ld64/unit-tests/test-cases/cpu-sub-types/Makefile | 34 +- ld64/unit-tests/test-cases/data-in-code/Makefile | 9 +- ld64/unit-tests/test-cases/data-in-code/main.c | 49 - ld64/unit-tests/test-cases/data-in-code/test.s | 14 +- ld64/unit-tests/test-cases/dso_handle/Makefile | 2 +- .../unit-tests/test-cases/eh-coalescing-r/Makefile | 3 +- .../test-cases/linker-optimization-hints/AdrpAdd.s | 27 + .../linker-optimization-hints/AdrpAddLdr.s | 55 + .../linker-optimization-hints/AdrpAddStr.s | 44 + .../test-cases/linker-optimization-hints/AdrpLdr.s | 64 + .../linker-optimization-hints/AdrpLdrGot.s | 30 + .../linker-optimization-hints/AdrpLdrGotLdr.s | 56 + .../linker-optimization-hints/AdrpLdrGotStr.s | 49 + .../test-cases/linker-optimization-hints/Makefile | 1418 +++++++++++++++ .../test-cases/linker-optimization-hints/main.s | 6 + .../Makefile | 22 +- .../test-cases/linker_options-framework/foo.c | 3 + .../test-cases/linker_options-framework/main.c | 8 + .../Makefile | 23 +- .../test-cases/linker_options-library/bar.c | 1 + .../test-cases/linker_options-library/foo.c | 3 + .../test-cases/linker_options-library/main.c | 10 + .../{data-in-code => lto-dynamic_export}/Makefile | 21 +- .../test-cases/lto-dynamic_export/main.c | 14 + ld64/unit-tests/test-cases/objc-abi/Makefile | 6 +- ld64/unit-tests/test-cases/objc-abi/test.m | 2 +- .../objc-category-optimize-load/Makefile | 2 +- .../test-cases/objc-category-optimize/Makefile | 2 +- .../test-cases/objc-category-warning/Makefile | 2 +- ld64/unit-tests/test-cases/objc-gc-checks/Makefile | 40 +- ld64/unit-tests/test-cases/objc-gc-checks/bar.m | 3 +- ld64/unit-tests/test-cases/objc-gc-checks/foo.m | 3 +- .../test-cases/objc-literal-pointers-strip/test.m | 4 +- ld64/unit-tests/test-cases/objc-properties/test.m | 2 +- ld64/unit-tests/test-cases/operator-new/main.cxx | 2 +- .../Makefile | 20 +- .../unit-tests/test-cases/order_file-archive/foo.c | 4 + .../test.m => order_file-archive/main.c} | 25 +- .../test-cases/order_file-archive/main.expected | 4 + .../test-cases/order_file-archive/main.order | 5 + .../test-cases/re-export-symbol/Makefile | 6 +- ld64/unit-tests/test-cases/re-export-symbol/foo.c | 19 +- .../unit-tests/test-cases/relocs-literals/Makefile | 2 +- ld64/unit-tests/test-cases/relocs-literals/test.c | 4 +- .../test-cases/relocs-literals2/Makefile | 2 +- ld64/unit-tests/test-cases/relocs-literals2/test.c | 4 +- ld64/unit-tests/test-cases/relocs-objc/test.m | 2 +- 103 files changed, 8785 insertions(+), 1164 deletions(-) create mode 100644 ld64/src/ld/passes/stubs/stub_arm64.hpp create mode 100644 ld64/unit-tests/test-cases/branch-long/Makefile create mode 100644 ld64/unit-tests/test-cases/branch-long/bar.s create mode 100644 ld64/unit-tests/test-cases/branch-long/foo.c copy ld64/unit-tests/test-cases/{eh-coalescing-r => coalesce-force}/Makefile (61%) create mode 100644 ld64/unit-tests/test-cases/coalesce-force/foo.c create mode 100644 ld64/unit-tests/test-cases/coalesce-force/foo.exp delete mode 100644 ld64/unit-tests/test-cases/data-in-code/main.c create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/Makefile create mode 100644 ld64/unit-tests/test-cases/linker-optimization-hints/main.s copy ld64/unit-tests/test-cases/{branch-islands => linker_options-framework}/Makefile (67%) create mode 100644 ld64/unit-tests/test-cases/linker_options-framework/foo.c create mode 100644 ld64/unit-tests/test-cases/linker_options-framework/main.c copy ld64/unit-tests/test-cases/{branch-islands => linker_options-library}/Makefile (65%) create mode 100644 ld64/unit-tests/test-cases/linker_options-library/bar.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library/foo.c create mode 100644 ld64/unit-tests/test-cases/linker_options-library/main.c copy ld64/unit-tests/test-cases/{data-in-code => lto-dynamic_export}/Makefile (71%) create mode 100644 ld64/unit-tests/test-cases/lto-dynamic_export/main.c copy ld64/unit-tests/test-cases/{branch-islands => order_file-archive}/Makefile (68%) create mode 100644 ld64/unit-tests/test-cases/order_file-archive/foo.c copy ld64/unit-tests/test-cases/{objc-literal-pointers-strip/test.m => order_file-archive/main.c} (75%) create mode 100644 ld64/unit-tests/test-cases/order_file-archive/main.expected create mode 100644 ld64/unit-tests/test-cases/order_file-archive/main.order diff --git a/ld64/doc/man/man1/ld.1 b/ld64/doc/man/man1/ld.1 index c2eb01d..964d099 100644 --- a/ld64/doc/man/man1/ld.1 +++ b/ld64/doc/man/man1/ld.1 @@ -297,6 +297,10 @@ them to use no space on disk. This option suppresses that optimization, so zero space on disk in a final linked image. .It Fl merge_zero_fill_sections Causes all zero-fill sections in the __DATA segment to be merged into one __zerofill section. +.It Fl no_branch_islands +Disables linker creation of branch islands which allows images to be created that are larger than the +maximum branch distance. Useful with -preload when code is in multiple sections but all are within +the branch range. .El .Ss Options when creating a dynamic library (dylib) .Bl -tag @@ -357,6 +361,10 @@ is a hexadecimal number with an optional leading 0x. The should be an even multiple of 4KB, that is the last three hexadecimal digits should be zero. .It Fl allow_stack_execute Marks executable so that all stacks in the task will be given stack execution privilege. This includes pthread stacks. +.It Fl export_dynamic +Preserves all global symbols in main executables during LTO. Without this option, Link Time Optimization +is allowed to inline and remove global functions. This option is used when a main executable may load +a plug-in which requires certain symbols from the main executable. .El .Ss Options when creating a bundle .Bl -tag @@ -376,6 +384,8 @@ Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather leave them as private external in the resulting object file. .It Fl d Force definition of common symbols. That is, transform tentative definitions into real definitions. +.It Fl rename_section Ar fromSegment fromSection toSegment toSection +Renames section fromSegment/fromSection to toSegment/toSection. .El .Ss Options that control symbol resolution .Bl -tag @@ -695,7 +705,7 @@ order file to cluster commonly used/dirty globals onto the same page(s). .Ss Obsolete Options .Bl -tag .It Fl segalign Ar value -All segments must be page aligned. This option is obsolete. +All segments must be page aligned. .It Fl seglinkedit Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This option is obsolete. .It Fl noseglinkedit @@ -703,7 +713,7 @@ This is the default. This option is obsolete. .It Fl fvmlib Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. .It Fl preload -Preload executables (MH_PRELOAD) are no longer supported. This option is obsolete. +Preload executables (MH_PRELOAD) are no longer supported. .It Fl sectobjectsymbols Ar segname Ar sectname Adding a local label at a section start is no longer supported. This option is obsolete. .It Fl nofixprebinding diff --git a/ld64/ld64.xcodeproj/project.pbxproj b/ld64/ld64.xcodeproj/project.pbxproj index dd74668..250fa5e 100644 --- a/ld64/ld64.xcodeproj/project.pbxproj +++ b/ld64/ld64.xcodeproj/project.pbxproj @@ -87,7 +87,7 @@ /* Begin PBXBuildRule section */ F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.llvm.clang.1_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.c; isEditable = 1; outputFiles = ( @@ -248,8 +248,9 @@ F9023C3F06D5A254001BBF46 /* ld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ld.cpp; path = src/ld/ld.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F92D9C2710657AAB00FF369B /* stub_x86_64_classic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_x86_64_classic.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933D9460929277C0083EAC8 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = src/abstraction/FileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F933D9470929277C0083EAC8 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = src/abstraction/MachOFileAbstraction.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F933DC37092A82480083EAC8 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = src/ld/Architectures.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F93A9BEC12C2E51900BAA11D /* stub_arm64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.h; path = stub_arm64.hpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F93CB246116E69EB003233B8 /* tlvp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tlvp.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F93CB247116E69EB003233B8 /* tlvp.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = tlvp.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F971EED306D5ACF60041D381 /* ObjectDump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjectDump; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -312,8 +313,8 @@ F9BA955D10A233000097A440 /* huge.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = huge.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9BA963310A2545C0097A440 /* compact_unwind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = compact_unwind.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9BA963410A2545C0097A440 /* compact_unwind.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = compact_unwind.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48A06DD1E1B001C7193 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = src/ld/Options.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + F9C0D48B06DD1E1B001C7193 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Options.h; path = src/ld/Options.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9C12E9F0ED63DB1005BC69D /* dyldinfo.1 */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text.man; name = dyldinfo.1; path = doc/man/man1/dyldinfo.1; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9CC24141461FB4300A92174 /* blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blob.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9CC24151461FB4300A92174 /* blob.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = blob.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; @@ -436,6 +437,7 @@ F9AA65101051BD2B003E3539 /* stubs.cpp */, F9AA650F1051BD2B003E3539 /* stub_arm.hpp */, F984A13B10B614CF009E9878 /* stub_arm_classic.hpp */, + F93A9BEC12C2E51900BAA11D /* stub_arm64.hpp */, F9BA8A7F1096150F0097A440 /* stub_x86.hpp */, F9BA8A7E1096150F0097A440 /* stub_x86_classic.hpp */, F989D0391062E6350014B60C /* stub_x86_64.hpp */, @@ -1056,6 +1058,7 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; OTHER_CPLUSPLUSFLAGS = ( @@ -1064,7 +1067,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1122,13 +1125,14 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CPLUSPLUSFLAGS)", ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1171,7 +1175,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1206,7 +1210,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1324,13 +1328,14 @@ "$(DEVELOPER_DIR)/usr/include", ); INSTALL_PATH = /usr/bin; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CPLUSPLUSFLAGS)", ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1413,7 +1418,7 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", - "@$(DERIVED_SOURCES_DIR)/LTO_option.txt", + "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", ); OTHER_REZFLAGS = ""; PREBINDING = NO; @@ -1479,6 +1484,10 @@ GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; @@ -1504,6 +1513,10 @@ GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; @@ -1519,6 +1532,10 @@ GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PREBINDING = NO; PRODUCT_NAME = prunetrie; }; diff --git a/ld64/src/abstraction/MachOFileAbstraction.hpp b/ld64/src/abstraction/MachOFileAbstraction.hpp index 4fd6123..9279734 100644 --- a/ld64/src/abstraction/MachOFileAbstraction.hpp +++ b/ld64/src/abstraction/MachOFileAbstraction.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "FileAbstraction.hpp" @@ -205,7 +206,7 @@ #ifndef LC_DATA_IN_CODE #define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */ - struct data_in_code_entry { + struct data_in_code_entry { uint32_t offset; uint16_t length; uint16_t kind; @@ -216,6 +217,20 @@ #define LC_DYLIB_CODE_SIGN_DRS 0x2B #endif +#ifndef LC_ENCRYPTION_INFO_64 + #define LC_ENCRYPTION_INFO_64 0x2C + struct encryption_info_command_64 { + uint32_t cmd; + uint32_t cmdsize; + uint32_t cryptoff; + uint32_t cryptsize; + uint32_t cryptid; + uint32_t pad; + }; +#endif + + + #ifndef CPU_SUBTYPE_ARM_V7F #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) #endif @@ -227,6 +242,124 @@ #endif + +// hack until arm64 headers are worked out +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) +#define CPU_SUBTYPE_ARM64_ALL 0 +#define CPU_SUBTYPE_ARM64_V8 1 + +#define ARM64_RELOC_UNSIGNED 0 // for pointers +#define ARM64_RELOC_SUBTRACTOR 1 // must be followed by a ARM64_RELOC_UNSIGNED +#define ARM64_RELOC_BRANCH26 2 // a B/BL instruction with 26-bit displacement +#define ARM64_RELOC_PAGE21 3 // pc-rel distance to page of target +#define ARM64_RELOC_PAGEOFF12 4 // offset within page, scaled by r_length +#define ARM64_RELOC_GOT_LOAD_PAGE21 5 // pc-rel distance to page of GOT slot +#define ARM64_RELOC_GOT_LOAD_PAGEOFF12 6 // offset within page of GOT slot, scaled by r_length +#define ARM64_RELOC_POINTER_TO_GOT 7 // for pointers to GOT slots +#define ARM64_RELOC_TLVP_LOAD_PAGE21 8 // pc-rel distance to page of TLVP slot +#define ARM64_RELOC_TLVP_LOAD_PAGEOFF12 9 // offset within page of TLVP slot, scaled by r_length +#define ARM64_RELOC_ADDEND 10 // r_symbolnum is addend for next reloc + + + +#define UNW_ARM64_X0 0 +#define UNW_ARM64_X1 1 +#define UNW_ARM64_X2 2 +#define UNW_ARM64_X3 3 +#define UNW_ARM64_X4 4 +#define UNW_ARM64_X5 5 +#define UNW_ARM64_X6 6 +#define UNW_ARM64_X7 7 +#define UNW_ARM64_X8 8 +#define UNW_ARM64_X9 9 +#define UNW_ARM64_X10 10 +#define UNW_ARM64_X11 11 +#define UNW_ARM64_X12 12 +#define UNW_ARM64_X13 13 +#define UNW_ARM64_X14 14 +#define UNW_ARM64_X15 15 +#define UNW_ARM64_X16 16 +#define UNW_ARM64_X17 17 +#define UNW_ARM64_X18 18 +#define UNW_ARM64_X19 19 +#define UNW_ARM64_X20 20 +#define UNW_ARM64_X21 21 +#define UNW_ARM64_X22 22 +#define UNW_ARM64_X23 23 +#define UNW_ARM64_X24 24 +#define UNW_ARM64_X25 25 +#define UNW_ARM64_X26 26 +#define UNW_ARM64_X27 27 +#define UNW_ARM64_X28 28 +#define UNW_ARM64_X29 29 +#define UNW_ARM64_FP 29 +#define UNW_ARM64_X30 30 +#define UNW_ARM64_LR 30 +#define UNW_ARM64_X31 31 +#define UNW_ARM64_SP 31 +#define UNW_ARM64_D0 64 +#define UNW_ARM64_D1 65 +#define UNW_ARM64_D2 66 +#define UNW_ARM64_D3 67 +#define UNW_ARM64_D4 68 +#define UNW_ARM64_D5 69 +#define UNW_ARM64_D6 70 +#define UNW_ARM64_D7 71 +#define UNW_ARM64_D8 72 +#define UNW_ARM64_D9 73 +#define UNW_ARM64_D10 74 +#define UNW_ARM64_D11 75 +#define UNW_ARM64_D12 76 +#define UNW_ARM64_D13 77 +#define UNW_ARM64_D14 78 +#define UNW_ARM64_D15 79 +#define UNW_ARM64_D16 80 +#define UNW_ARM64_D17 81 +#define UNW_ARM64_D18 82 +#define UNW_ARM64_D19 83 +#define UNW_ARM64_D20 84 +#define UNW_ARM64_D21 85 +#define UNW_ARM64_D22 86 +#define UNW_ARM64_D23 87 +#define UNW_ARM64_D24 88 +#define UNW_ARM64_D25 89 +#define UNW_ARM64_D26 90 +#define UNW_ARM64_D27 91 +#define UNW_ARM64_D28 92 +#define UNW_ARM64_D29 93 +#define UNW_ARM64_D30 94 +#define UNW_ARM64_D31 95 + +#define UNWIND_ARM64_MODE_MASK 0x0F000000 +#define UNWIND_ARM64_MODE_FRAME_OLD 0x01000000 +#define UNWIND_ARM64_MODE_FRAMELESS 0x02000000 +#define UNWIND_ARM64_MODE_DWARF 0x03000000 +#define UNWIND_ARM64_MODE_FRAME 0x04000000 + +#define UNWIND_ARM64_FRAME_X19_X20_PAIR 0x00000001 +#define UNWIND_ARM64_FRAME_X21_X22_PAIR 0x00000002 +#define UNWIND_ARM64_FRAME_X23_X24_PAIR 0x00000004 +#define UNWIND_ARM64_FRAME_X25_X26_PAIR 0x00000008 +#define UNWIND_ARM64_FRAME_X27_X28_PAIR 0x00000010 +#define UNWIND_ARM64_FRAME_D8_D9_PAIR 0x00000100 +#define UNWIND_ARM64_FRAME_D10_D11_PAIR 0x00000200 +#define UNWIND_ARM64_FRAME_D12_D13_PAIR 0x00000400 +#define UNWIND_ARM64_FRAME_D14_D15_PAIR 0x00000800 + +#define UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK 0x00FFF000 + +#define UNWIND_ARM64_FRAME_X21_X22_PAIR_OLD 0x00000001 +#define UNWIND_ARM64_FRAME_X23_X24_PAIR_OLD 0x00000002 +#define UNWIND_ARM64_FRAME_X25_X26_PAIR_OLD 0x00000004 +#define UNWIND_ARM64_FRAME_X27_X28_PAIR_OLD 0x00000008 +#define UNWIND_ARM64_FRAME_D8_D9_PAIR_OLD 0x00000010 +#define UNWIND_ARM64_FRAME_D10_D11_PAIR_OLD 0x00000020 +#define UNWIND_ARM64_FRAME_D12_D13_PAIR_OLD 0x00000040 +#define UNWIND_ARM64_FRAME_D14_D15_PAIR_OLD 0x00000080 + + +#define UNWIND_ARM64_DWARF_SECTION_OFFSET 0x00FFFFFF + #ifndef LC_SOURCE_VERSION #define LC_SOURCE_VERSION 0x2A struct source_version_command { @@ -250,6 +383,53 @@ #define LC_DYLIB_CODE_SIGN_DRS 0x2B #endif +#ifndef LC_LINKER_OPTION + #define LC_LINKER_OPTION 0x2D + + struct linker_option_command { + uint32_t cmd; /*LC_LINKER_OPTION only used in MH_OBJECT filetypes */ + uint32_t cmdsize; + uint32_t count; /* number of strings */ + /* concatenation of zero terminated UTF8 strings. Zero filled at end to align */ + }; +#endif + +#ifndef LC_LINKER_OPTIMIZATION_HINTS + #define LC_LINKER_OPTIMIZATION_HINTS 0x2E + #define LOH_ARM64_ADRP_ADRP 1 + #define LOH_ARM64_ADRP_LDR 2 + #define LOH_ARM64_ADRP_ADD_LDR 3 + #define LOH_ARM64_ADRP_LDR_GOT_LDR 4 + #define LOH_ARM64_ADRP_ADD_STR 5 + #define LOH_ARM64_ADRP_LDR_GOT_STR 6 + #define LOH_ARM64_ADRP_ADD 7 + #define LOH_ARM64_ADRP_LDR_GOT 8 +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + + +#ifndef CPU_SUBTYPE_ARM_V8 + #define CPU_SUBTYPE_ARM_V8 ((cpu_subtype_t) 13) +#endif + +#ifndef CPU_SUBTYPE_ARM_V6M + #define CPU_SUBTYPE_ARM_V6M ((cpu_subtype_t) 14) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7M + #define CPU_SUBTYPE_ARM_V7M ((cpu_subtype_t) 15) +#endif + +#ifndef CPU_SUBTYPE_ARM_V7EM + #define CPU_SUBTYPE_ARM_V7EM ((cpu_subtype_t) 16) +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif struct ArchInfo { const char* archName; @@ -263,7 +443,10 @@ struct ArchInfo { static const ArchInfo archInfoArray[] = { #if SUPPORT_ARCH_x86_64 - { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", false, false }, + { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, "x86_64-", "", true, false }, +#endif +#if SUPPORT_ARCH_x86_64h + { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H, "x86_64h-", "", true, false }, #endif #if SUPPORT_ARCH_i386 { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, "i386-", "", false, false }, @@ -296,10 +479,32 @@ static const ArchInfo archInfoArray[] = { { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S, "thumbv7s-", "armv7s", true, true }, #define SUPPORT_ARCH_arm_any 1 #endif +#if SUPPORT_ARCH_armv6m + { "armv6m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6M, "thumbv6m-", "", true, false }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7m + { "armv7m", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7M, "thumbv7m-", "armv7m", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv7em + { "armv7em", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7EM, "thumbv7em-", "armv7em", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_armv8 + { "armv8", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V8, "thumbv8-", "armv8", true, true }, + #define SUPPORT_ARCH_arm_any 1 +#endif +#if SUPPORT_ARCH_arm64 + { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, "arm64-", "", false, false }, +#endif +#if SUPPORT_ARCH_arm64v8 + { "arm64v8", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8, "arm64v8-", "", true, false }, +#endif { NULL, 0, 0, NULL, NULL, false, false } }; - + // weird, but this include must wait until after SUPPORT_ARCH_arm_any is set up #if SUPPORT_ARCH_arm_any #include @@ -1125,27 +1330,37 @@ 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 +1681,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/ld64/src/abstraction/MachOTrie.hpp b/ld64/src/abstraction/MachOTrie.hpp index 1885e48..b7c55c1 100644 --- a/ld64/src/abstraction/MachOTrie.hpp +++ b/ld64/src/abstraction/MachOTrie.hpp @@ -329,7 +329,7 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* { if ( p >= end ) throw "malformed trie, node past end"; - const uint8_t terminalSize = read_uleb128(p, end); + const uint64_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { EntryWithOffset e; diff --git a/ld64/src/create_configure b/ld64/src/create_configure index 447ec7a..8ca92be 100755 --- a/ld64/src/create_configure +++ b/ld64/src/create_configure @@ -11,35 +11,22 @@ else fi if [ -z "${RC_SUPPORTED_ARCHS}" ]; then - RC_SUPPORTED_ARCHS="i386 x86_64 armv7 armv7s" + RC_SUPPORTED_ARCHS="i386 x86_64 x86_64h armv6 armv7 armv7s armv7m arm64" fi for ANARCH in ${RC_SUPPORTED_ARCHS} do - KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,i386,x86_64," + KNOWN_ARCHS=",armv4t,armv5,armv6,armv7,armv7f,armv7k,armv7s,armv6m,armv7m,armv7em,armv8,arm64,arm64v8,i386,x86_64,x86_64h," FOUND=`echo "$KNOWN_ARCHS" | grep ",$ANARCH,"` if [ $FOUND ]; then echo "#define SUPPORT_ARCH_$ANARCH 1" >> ${DERIVED_FILE_DIR}/configure.h else - echo "#error uknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h + echo "#error unknown architecture: $ANARCH" >> ${DERIVED_FILE_DIR}/configure.h fi done echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h -# ld64 hardcodes a reference to /Developer/usr/lib/libLTO.dylib -if [ -n "${DT_TOOLCHAIN_DIR}" ] -then - echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt -else - if [ -e "/Developer/usr/lib/libLTO.dylib" ] - then - echo "-Wl,-lazy_library,/Developer/usr/lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt - else - echo "-Wl,-lazy_library,${BUILT_PRODUCTS_DIR}/../lib/libLTO.dylib" > ${DERIVED_SOURCES_DIR}/LTO_option.txt - fi -fi - diff --git a/ld64/src/ld/Architectures.hpp b/ld64/src/ld/Architectures.hpp index 1145550..fdf795b 100644 --- a/ld64/src/ld/Architectures.hpp +++ b/ld64/src/ld/Architectures.hpp @@ -56,6 +56,11 @@ struct arm typedef Pointer32 P; }; +struct arm64 +{ + typedef Pointer64 P; +}; + #endif // __ARCHITECTURES__ diff --git a/ld64/src/ld/HeaderAndLoadCommands.hpp b/ld64/src/ld/HeaderAndLoadCommands.hpp index 0885715..acbdf4f 100644 --- a/ld64/src/ld/HeaderAndLoadCommands.hpp +++ b/ld64/src/ld/HeaderAndLoadCommands.hpp @@ -109,7 +109,9 @@ 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; + uint8_t* copyOptimizationHintsLoadCommand(uint8_t* p) const; + uint32_t sectionFlags(ld::Internal::FinalSection* sect) const; bool sectionTakesNoDiskSpace(ld::Internal::FinalSection* sect) const; @@ -136,6 +138,7 @@ private: bool _hasDataInCodeLoadCommand; bool _hasSourceVersionLoadCommand; bool _hasDependentDRInfo; + bool _hasOptimizationHints; uint32_t _dylibLoadCommmandsCount; uint32_t _allowableClientLoadCommmandsCount; uint32_t _dyldEnvironExrasCount; @@ -143,6 +146,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; @@ -174,6 +178,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _hasRoutinesLoadCommand = (opts.initFunctionName() != NULL); _hasSymbolTableLoadCommand = true; _hasUUIDLoadCommand = (opts.UUIDMode() != Options::kUUIDNone); + _hasOptimizationHints = (_state.someObjectHasOptimizationHints && (opts.outputKind() == Options::kObjectFile)); switch ( opts.outputKind() ) { case Options::kDynamicExecutable: case Options::kDynamicLibrary: @@ -192,6 +197,22 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: break; } } + for (CStringSet::const_iterator it = _state.linkerOptionFrameworks.begin(); it != _state.linkerOptionFrameworks.end(); ++it) { + const char* frameWorkName = *it; + std::vector* lo = new std::vector(); + lo->push_back("-framework"); + lo->push_back(frameWorkName); + _linkerOptions.push_back(*lo); + }; + for (CStringSet::const_iterator it = _state.linkerOptionLibraries.begin(); it != _state.linkerOptionLibraries.end(); ++it) { + const char* libName = *it; + std::vector* lo = new std::vector(); + char * s = new char[strlen(libName)+3]; + strcpy(s, "-l"); + strcat(s, libName); + lo->push_back(s); + _linkerOptions.push_back(*lo); + }; break; case Options::kStaticExecutable: _hasDynamicSymbolTableLoadCommand = opts.positionIndependentExecutable(); @@ -210,6 +231,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: _dylibLoadCommmandsCount = _writer.dylibCount(); _allowableClientLoadCommmandsCount = _options.allowableClients().size(); _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); + if ( ! _options.useSimplifiedDylibReExports() ) { // target OS does not support LC_REEXPORT_DYLIB, so use old complicated load commands for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { @@ -394,8 +416,22 @@ uint64_t HeaderAndLoadCommandsAtom::size() const if ( _hasDataInCodeLoadCommand ) sz += sizeof(macho_linkedit_data_command

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

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

); + + if ( _hasOptimizationHints ) + sz += sizeof(macho_linkedit_data_command

); return sz; } @@ -465,8 +501,17 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const if ( _hasDataInCodeLoadCommand ) ++count; + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + ++count; + } + } + if ( _hasDependentDRInfo ) ++count; + + if ( _hasOptimizationHints ) + ++count; return count; } @@ -525,9 +570,9 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const bits |= MH_FORCE_FLAT; break; } - if ( _writer.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols ) + if ( _state.hasWeakExternalSymbols || _writer.overridesWeakExternalSymbols ) bits |= MH_WEAK_DEFINES; - if ( _writer.usesWeakExternalSymbols || _writer.hasWeakExternalSymbols ) + if ( _writer.usesWeakExternalSymbols || _state.hasWeakExternalSymbols ) bits |= MH_BINDS_TO_WEAK; if ( _options.prebind() ) bits |= MH_PREBOUND; @@ -542,7 +587,7 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const bits |= MH_PIE; if ( _options.markAutoDeadStripDylib() ) bits |= MH_DEAD_STRIPPABLE_DYLIB; - if ( _writer.hasThreadLocalVariableDefinitions ) + if ( _state.hasThreadLocalVariableDefinitions ) bits |= MH_HAS_TLV_DESCRIPTORS; if ( _options.hasNonExecutableHeap() ) bits |= MH_NO_HEAP_EXECUTION; @@ -556,10 +601,12 @@ uint32_t HeaderAndLoadCommandsAtom::flags() const template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC; } +template <> uint32_t HeaderAndLoadCommandsAtom::magic() const { return MH_MAGIC_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_I386; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_X86_64; } template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM; } +template <> uint32_t HeaderAndLoadCommandsAtom::cpuType() const { return CPU_TYPE_ARM64; } @@ -572,10 +619,10 @@ uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const template <> uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const { - if ( (_options.outputKind() == Options::kDynamicExecutable) && (_options.macosxVersionMin() >= ld::mac10_5) ) - return (CPU_SUBTYPE_X86_64_ALL | 0x80000000); + if ( (_options.outputKind() == Options::kDynamicExecutable) && (_state.cpuSubType == CPU_SUBTYPE_X86_64_ALL) && (_options.macosxVersionMin() >= ld::mac10_5) ) + return (_state.cpuSubType | 0x80000000); else - return CPU_SUBTYPE_X86_64_ALL; + return _state.cpuSubType; } template <> @@ -584,6 +631,12 @@ uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const return _state.cpuSubType; } +template <> +uint32_t HeaderAndLoadCommandsAtom::cpuSubType() const +{ + return CPU_SUBTYPE_ARM64_ALL; +} + template @@ -1144,6 +1197,28 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) cons } +template <> +uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const +{ + return this->alignedSize(16 + 34 * 8); // base size + ARM_EXCEPTION_STATE64_COUNT * 4 +} + +template <> +uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) const +{ + assert(_state.entryPoint != NULL); + pint_t start = _state.entryPoint->finalAddress(); + macho_thread_command

* cmd = (macho_thread_command

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

* cmd = (macho_encryption_info_command

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

)); assert(_writer.encryptedTextStartOffset() != 0); assert(_writer.encryptedTextEndOffset() != 0); @@ -1311,6 +1386,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 { macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)p; @@ -1322,6 +1418,19 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) co } + +template +uint8_t* HeaderAndLoadCommandsAtom::copyOptimizationHintsLoadCommand(uint8_t* p) const +{ + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)p; + cmd->set_cmd(LC_LINKER_OPTIMIZATION_HINTS); + cmd->set_cmdsize(sizeof(macho_linkedit_data_command

)); + cmd->set_dataoff(_writer.optimizationHintsSection->fileOffset); + cmd->set_datasize(_writer.optimizationHintsSection->size); + return p + sizeof(macho_linkedit_data_command

); +} + + template void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const { @@ -1384,7 +1493,7 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasSplitSegInfoLoadCommand ) p = this->copySplitSegInfoLoadCommand(p); - for(uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { + for (uint32_t ord=1; ord <= _writer.dylibCount(); ++ord) { p = this->copyDylibLoadCommand(p, _writer.dylibByOrdinal(ord)); } @@ -1425,10 +1534,19 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const if ( _hasDataInCodeLoadCommand ) p = this->copyDataInCodeLoadCommand(p); + + if ( !_linkerOptions.empty() ) { + for (ld::relocatable::File::LinkerOptionsList::const_iterator it = _linkerOptions.begin(); it != _linkerOptions.end(); ++it) { + p = this->copyLinkerOptionsLoadCommand(p, *it); + } + } if ( _hasDependentDRInfo ) p = this->copyDependentDRLoadCommand(p); + if ( _hasOptimizationHints ) + p = this->copyOptimizationHintsLoadCommand(p); + } diff --git a/ld64/src/ld/InputFiles.cpp b/ld64/src/ld/InputFiles.cpp index 7bf136b..f49f2e3 100644 --- a/ld64/src/ld/InputFiles.cpp +++ b/ld64/src/ld/InputFiles.cpp @@ -1,3 +1,4 @@ + /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* * * Copyright (c) 2009-2011 Apple Inc. All rights reserved. @@ -60,6 +61,7 @@ #include "archive_file.h" #include "lto_file.h" #include "opaque_section_file.h" +#include "MachOFileAbstraction.hpp" #include "Snapshot.h" const bool _s_logPThreads = false; @@ -180,7 +182,11 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) const char* result = mach_o::relocatable::archName(p); if ( result != NULL ) return result; - + + result = mach_o::dylib::archName(p); + if ( result != NULL ) + return result; + result = lto::archName(p, len); if ( result != NULL ) return result; @@ -192,7 +198,7 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) strcpy(unsupported, "unsupported file format ("); for (unsigned i=0; i(file); + if ( file == _bundleLoader ) { + _options.dumpDependency(Options::depBundleLoader, file->path()); + } + else if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) { + if ( indirect ) + _options.dumpDependency(Options::depUpwardIndirectDylib, file->path()); + else + _options.dumpDependency(Options::depUpwardDirectDylib, file->path()); + } + else { + if ( indirect ) + _options.dumpDependency(Options::depIndirectDylib, file->path()); + else + _options.dumpDependency(Options::depDirectDylib, file->path()); + } + } } void InputFiles::logArchive(ld::File* file) const @@ -396,7 +425,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const if ( trace_file_path != NULL ) { trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); if ( trace_file == -1 ) - throwf("Could not open or create trace file: %s", trace_file_path); + throwf("Could not open or create trace file (errno=%d): %s", errno, trace_file_path); } else { trace_file = fileno(stderr); @@ -420,6 +449,7 @@ void InputFiles::logTraceInfo(const char* format, ...) const } } + ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* fromPath) { //fprintf(stderr, "findDylib(%s, %s)\n", installPath, fromPath); @@ -435,6 +465,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from Options::FileInfo info = _options.findFile(dit->useInstead); _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); info.ordinal = _indirectDylibOrdinal; + info.options.fIndirectDylib = true; ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { @@ -467,6 +498,7 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from Options::FileInfo info = _options.findFileUsingPaths(installPath); _indirectDylibOrdinal = _indirectDylibOrdinal.nextIndirectDylibOrdinal(); info.ordinal = _indirectDylibOrdinal; + info.options.fIndirectDylib = true; try { ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); @@ -487,18 +519,86 @@ ld::dylib::File* InputFiles::findDylib(const char* installPath, const char* from } - -void InputFiles::createIndirectDylibs() -{ - _allDirectDylibsLoaded = true; - _indirectDylibOrdinal = ld::File::Ordinal::indirectDylibBase(); - - // mark all dylibs initially specified as required and check if they can be used +// mark all dylibs initially specified as required, and check if they can be used +void InputFiles::markExplicitlyLinkedDylibs() +{ for (InstallNameToDylib::iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); it++) { it->second->setExplicitlyLinked(); this->checkDylibClientRestrictions(it->second); } - +} + +bool InputFiles::libraryAlreadyLoaded(const char* path) +{ + for (std::vector::const_iterator it = _inputFiles.begin(); it != _inputFiles.end(); ++it) { + if ( strcmp(path, (*it)->path()) == 0 ) + return true; + } + return false; +} + + +void InputFiles::addLinkerOptionLibraries(ld::Internal& state) +{ + if ( _options.outputKind() == Options::kObjectFile ) + return; + + // process frameworks specified in .o linker options + for (CStringSet::const_iterator it = state.linkerOptionFrameworks.begin(); it != state.linkerOptionFrameworks.end(); ++it) { + const char* frameworkName = *it; + Options::FileInfo info = _options.findFramework(frameworkName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + try { + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->setImplicitlyLinked(); + this->addDylib(dylibReader, info); + } + } + else { + throwf("framework linker option at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + warning("Auto-Linking supplied '%s', %s", info.path, msg); + } + } + } + // process libraries specified in .o linker options + for (CStringSet::const_iterator it = state.linkerOptionLibraries.begin(); it != state.linkerOptionLibraries.end(); ++it) { + const char* libName = *it; + Options::FileInfo info = _options.findLibrary(libName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + info.ordinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + try { + ld::File* reader = this->makeFile(info, true); + ld::dylib::File* dylibReader = dynamic_cast(reader); + ld::archive::File* archiveReader = dynamic_cast(reader); + if ( dylibReader != NULL ) { + dylibReader->setImplicitlyLinked(); + this->addDylib(dylibReader, info); + } + else if ( archiveReader != NULL ) { + _searchLibraries.push_back(LibraryInfo(archiveReader)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depArchive, archiveReader->path()); + } + else { + throwf("linker option dylib at %s is not a dylib", info.path); + } + } + catch (const char* msg) { + warning("Auto-Linking supplied '%s', %s", info.path, msg); + } + } + } +} + +void InputFiles::createIndirectDylibs() +{ // keep processing dylibs until no more dylibs are added unsigned long lastMapSize = 0; std::set dylibsProcessed; @@ -539,9 +639,11 @@ void InputFiles::createIndirectDylibs() void InputFiles::createOpaqueFileSections() { - // extra command line section always at end + // extra command line sections always at end for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) { _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depSection, it->path); } } @@ -672,8 +774,10 @@ InputFiles::InputFiles(Options& opts, const char** archName) : _totalObjectSize(0), _totalArchiveSize(0), _totalObjectLoaded(0), _totalArchivesLoaded(0), _totalDylibsLoaded(0), _options(opts), _bundleLoader(NULL), - _allDirectDylibsLoaded(false), _inferredArch(false), - _exception(NULL) + _inferredArch(false), + _exception(NULL), + _indirectDylibOrdinal(ld::File::Ordinal::indirectDylibBase()), + _linkerOptionOrdinal(ld::File::Ordinal::linkeOptionBase()) { // fStartCreateReadersTime = mach_absolute_time(); if ( opts.architecture() == 0 ) { @@ -781,7 +885,8 @@ void InputFiles::parseWorkerThread() { if (_s_logPThreads) printf("parsing index %u\n", slot); try { file = makeFile(entry, false); - } catch (const char *msg) { + } + catch (const char *msg) { if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) { if ( _options.ignoreOtherArchInputFiles() ) { // ignore, because this is about an architecture not in use @@ -789,8 +894,9 @@ void InputFiles::parseWorkerThread() { else { warning("ignoring file %s, %s", entry.path, msg); } - } else { - exception = msg; + } + else { + asprintf((char**)&exception, "%s file '%s'", msg, entry.path); } file = new IgnoredFile(entry.path, entry.modTime, entry.ordinal, ld::File::Other); } @@ -802,7 +908,8 @@ void InputFiles::parseWorkerThread() { // We are about to die, so set to zero to stop other threads from doing unneeded work. _remainingInputFiles = 0; _exception = exception; - } else { + } + else { _inputFiles[slot] = file; if (_neededFileSlot == slot) pthread_cond_signal(&_newFileAvailable); @@ -867,22 +974,23 @@ ld::File* InputFiles::addDylib(ld::dylib::File* reader, const Options::FileInfo& } // remove warning for Same install name for CoreServices and CFNetwork? //if ( !dylibOnCommandLineTwice && !isSymlink ) - // warning("dylibs with same install name: %s and %s", pos->second->path(), reader->path()); + // warning("dylibs with same install name: %p %s and %p %s", pos->second, pos->second->path(), reader, reader->path()); } } else if ( info.options.fBundleLoader ) _bundleLoader = reader; // log direct readers - if ( !_allDirectDylibsLoaded ) + if ( ! info.options.fIndirectDylib ) this->logDylib(reader, false); // update stats _totalDylibsLoaded++; // just add direct libraries to search-first list - if ( !_allDirectDylibsLoaded ) + if ( ! info.options.fIndirectDylib ) _searchLibraries.push_back(LibraryInfo(reader)); + return reader; } @@ -918,7 +1026,7 @@ void InputFiles::waitForInputFiles() if (it == fileMap.end()) throwf("pipelined linking error - not in file list: %s\n", path_buf); Options::FileInfo* inputInfo = (Options::FileInfo*)it->second; - if (!inputInfo->checkFileExists()) + if (!inputInfo->checkFileExists(_options)) throwf("pipelined linking error - file does not exist: %s\n", inputInfo->path); pthread_mutex_lock(&_parseLock); if (_idleWorkers) @@ -946,7 +1054,7 @@ void InputFiles::waitForInputFiles(InputFiles *inputFiles) { #endif -void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) +void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal& state) { // add all direct object, archives, and dylibs const std::vector& files = _options.getInputFiles(); @@ -986,6 +1094,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) { ld::relocatable::File* reloc = (ld::relocatable::File*)file; _options.snapshot().recordObjectFile(reloc->path()); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depObjectFile, reloc->path()); } break; case ld::File::Dylib: @@ -1001,6 +1111,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() ) logArchive(archive); _searchLibraries.push_back(LibraryInfo(archive)); + if ( _options.dumpDependencyInfo() ) + _options.dumpDependency(Options::depArchive, archive->path()); } break; case ld::File::Other: @@ -1014,6 +1126,8 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler) file->forEachAtom(handler); } + markExplicitlyLinkedDylibs(); + addLinkerOptionLibraries(state); createIndirectDylibs(); createOpaqueFileSections(); @@ -1095,7 +1209,7 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc logArchive(archiveFile); _options.snapshot().recordArchive(archiveFile->path()); // found data definition in static library, done - return true; + return true; } } else { @@ -1145,7 +1259,7 @@ bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searc bool InputFiles::searchWeakDefInDylib(const char* name) const { - // search all relevant dylibs to see if any of a weak-def with this name + // search all relevant dylibs to see if any have a weak-def with this name for (InstallNameToDylib::const_iterator it=_installPathToDylibs.begin(); it != _installPathToDylibs.end(); ++it) { ld::dylib::File* dylibFile = it->second; if ( dylibFile->implicitlyLinked() || dylibFile->explicitlyLinked() ) { @@ -1224,4 +1338,3 @@ void InputFiles::dylibs(ld::Internal& state) } // namespace tool } // namespace ld - diff --git a/ld64/src/ld/InputFiles.h b/ld64/src/ld/InputFiles.h index 9b969ee..b1e85cb 100644 --- a/ld64/src/ld/InputFiles.h +++ b/ld64/src/ld/InputFiles.h @@ -63,7 +63,7 @@ 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; @@ -74,6 +74,9 @@ public: bool inferredArch() const { return _inferredArch; } + void addLinkerOptionLibraries(ld::Internal& state); + void createIndirectDylibs(); + // for -print_statistics volatile int64_t _totalObjectSize; volatile int64_t _totalArchiveSize; @@ -90,12 +93,13 @@ private: void logTraceInfo (const char* format, ...) const; void logDylib(ld::File*, bool indirect); void logArchive(ld::File*) const; - void createIndirectDylibs(); + void markExplicitlyLinkedDylibs(); void checkDylibClientRestrictions(ld::dylib::File*); void createOpaqueFileSections(); + bool libraryAlreadyLoaded(const char* path); // for pipelined linking - void waitForInputFiles(); + void waitForInputFiles(); static void waitForInputFiles(InputFiles *inputFiles); // for threaded input file processing @@ -111,7 +115,6 @@ 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 +135,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/ld64/src/ld/LinkEdit.hpp b/ld64/src/ld/LinkEdit.hpp index 1149f0d..3420626 100644 --- a/ld64/src/ld/LinkEdit.hpp +++ b/ld64/src/ld/LinkEdit.hpp @@ -1005,6 +1005,14 @@ void ExportInfoAtom::encode() const entries.push_back(entry); //fprintf(stderr, "re-export %s from lib %llu as %s\n", entry.importName, entry.other, entry.name); } + else if ( atom->definition() == ld::Atom::definitionAbsolute ) { + entry.name = atom->name(); + entry.flags = _options.canUseAbsoluteSymbols() ? EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE : EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + entry.address = address; + entry.other = other; + entry.importName = NULL; + entries.push_back(entry); + } else { if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->combine() == ld::Atom::combineByName) ) flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; @@ -1068,6 +1076,7 @@ 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"); @@ -1507,6 +1555,68 @@ void DependentDRAtom::encode() const +template +class OptimizationHintsAtom : public LinkEditAtom +{ +public: + OptimizationHintsAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { + assert(opts.outputKind() == Options::kObjectFile); + } + + // overrides of ld::Atom + virtual const char* name() const { return "linker optimization hints"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + static ld::Section _s_section; + +}; + +template +ld::Section OptimizationHintsAtom::_s_section("__LINKEDIT", "__opt_hints", ld::Section::typeLinkEdit, true); + +template +void OptimizationHintsAtom::encode() const +{ + if ( _state.someObjectHasOptimizationHints ) { + for (std::vector::iterator sit = _state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() != ld::Section::typeCode ) + continue; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + uint64_t address = atom->finalAddress(); + for (ld::Fixup::iterator fit = atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { + if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint) + continue; + ld::Fixup::LOH_arm64 extra; + extra.addend = fit->u.addend; + _encodedData.append_uleb128(extra.info.kind); + _encodedData.append_uleb128(extra.info.count+1); + _encodedData.append_uleb128((extra.info.delta1 << 2) + fit->offsetInAtom + address); + if ( extra.info.count > 0 ) + _encodedData.append_uleb128((extra.info.delta2 << 2) + fit->offsetInAtom + address); + if ( extra.info.count > 1 ) + _encodedData.append_uleb128((extra.info.delta3 << 2) + fit->offsetInAtom + address); + if ( extra.info.count > 2 ) + _encodedData.append_uleb128((extra.info.delta4 << 2) + fit->offsetInAtom + address); + } + } + } + + this->_encodedData.pad_to_size(sizeof(pint_t)); + } + + this->_encoded = true; +} + + } // namespace tool } // namespace ld diff --git a/ld64/src/ld/LinkEditClassic.hpp b/ld64/src/ld/LinkEditClassic.hpp index 60a4fa9..b9b1215 100644 --- a/ld64/src/ld/LinkEditClassic.hpp +++ b/ld64/src/ld/LinkEditClassic.hpp @@ -927,6 +927,9 @@ uint64_t ExternalRelocationsAtom::size() const return (_pointerLocations.size() + _callSiteLocations.size()) * sizeof(macho_relocation_info

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

reloc1; + macho_relocation_info

reloc2; + uint64_t address = entry.inAtom->finalAddress()+entry.offsetInAtom - sect->address; + bool external = entry.toTargetUsesExternalReloc; + uint32_t symbolNum = sectSymNum(external, entry.toTarget); + bool fromExternal = false; + uint32_t fromSymbolNum = 0; + if ( entry.fromTarget != NULL ) { + fromExternal = entry.fromTargetUsesExternalReloc; + fromSymbolNum = sectSymNum(fromExternal, entry.fromTarget); + } + + + switch ( entry.kind ) { + case ld::Fixup::kindStoreARM64Branch26: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_BRANCH26); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64Page21: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64Page21: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_PAGE21); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64PageOff12: + if ( entry.toAddend != 0 ) { + assert(entry.toAddend < 0x400000); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(entry.toAddend); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(false); + reloc2.set_r_type(ARM64_RELOC_ADDEND); + relocs.push_back(reloc2); + } + // fall into next case + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_PAGEOFF12); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGE21); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_GOT_LOAD_PAGEOFF12); + relocs.push_back(reloc1); + break; + + + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(3); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( entry.fromTarget != NULL ) { + // this is a pointer-diff + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + reloc2.set_r_address(address); + reloc2.set_r_symbolnum(fromSymbolNum); + reloc2.set_r_pcrel(false); + reloc2.set_r_length(2); + reloc2.set_r_extern(fromExternal); + reloc2.set_r_type(ARM64_RELOC_SUBTRACTOR); + relocs.push_back(reloc2); + relocs.push_back(reloc1); + } + else { + // regular pointer + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_UNSIGNED); + relocs.push_back(reloc1); + } + break; + + case ld::Fixup::kindStoreARM64PointerToGOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(3); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64PCRelToGOT: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_POINTER_TO_GOT); + relocs.push_back(reloc1); + break; + + default: + assert(0 && "need to handle arm64 -r reloc"); + + } + +} +#endif // SUPPORT_ARCH_arm64 template diff --git a/ld64/src/ld/Options.cpp b/ld64/src/ld/Options.cpp index 0131911..057fad9 100644 --- a/ld64/src/ld/Options.cpp +++ b/ld64/src/ld/Options.cpp @@ -104,19 +104,24 @@ void throwf(const char* format, ...) throw t; } -bool Options::FileInfo::checkFileExists(const char *p) + +bool Options::FileInfo::checkFileExists(const Options& options, const char *p) { struct stat statBuffer; - if (p == NULL) p = path; + if (p == NULL) + p = path; if ( stat(p, &statBuffer) == 0 ) { if (p != path) path = strdup(p); fileLen = statBuffer.st_size; modTime = statBuffer.st_mtime; return true; } + if ( options.dumpDependencyInfo() ) + options.dumpDependency(Options::depNotFound, p); return false; } + Options::Options(int argc, const char* argv[]) : fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable), fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false), @@ -131,7 +136,7 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), + fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), @@ -167,9 +172,15 @@ Options::Options(int argc, const char* argv[]) fSourceVersionLoadCommand(false), fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), fDependentDRInfo(false), fDependentDRInfoForcedOn(false), fDependentDRInfoForcedOff(false), + fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false), + fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), + fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), + fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), + fGenerateDtraceDOF(true), fAllowBranchIslands(true), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), - fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL) + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -177,10 +188,18 @@ Options::Options(int argc, const char* argv[]) this->parsePostCommandLineEnvironmentSettings(); this->reconfigureDefaults(); this->checkIllegalOptionCombinations(); + + if ( this->dumpDependencyInfo() ) { + this->dumpDependency(depOutputFile, fOutputFile); + if ( fMapPath != NULL ) + this->dumpDependency(depOutputFile, fMapPath); + } } Options::~Options() { + if ( fDependencyFileDescriptor != -1 ) + ::close(fDependencyFileDescriptor); } bool Options::errorBecauseOfWarnings() const @@ -246,7 +265,11 @@ bool Options::allGlobalsAreDeadStripRoots() const // switch ( fOutputKind ) { case Options::kDynamicExecutable: + // Add the -export_dynamic flag + return fExportDynamic; case Options::kStaticExecutable: + // Support the -export_dynamic flag for xnu + return fExportDynamic; case Options::kPreload: // by default unused globals in a main executable are stripped return false; @@ -276,7 +299,6 @@ const char* Options::executablePath() return fExecutablePath; } - uint32_t Options::initialSegProtection(const char* segName) const { for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -478,6 +500,11 @@ bool Options::forceNotWeakNonWildcard(const char* symbolName) const return fForceNotWeakSymbols.containsNonWildcard(symbolName); } +bool Options::forceCoalesce(const char* symbolName) const +{ + return fForceCoalesceSymbols.contains(symbolName); +} + bool Options::shouldExport(const char* symbolName) const { @@ -533,27 +560,29 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fMacVersionMin = ld::mac10_6; #endif } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; break; case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); - #elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else - warning("-macosx_version_min not specified, assuming 10.6"); - fMacVersionMin = ld::mac10_6; + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); #endif } - if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) - fMakeCompressedDyldInfo = true; break; } fLinkSnapshot.recordArch(fArchitectureName); + // only use compressed LINKEDIT for: + // Mac OS X 10.6 or later + // iOS 3.1 or later + if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff ) + fMakeCompressedDyldInfo = true; + // Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB + if ( minOS(ld::mac10_5, ld::iOS_2_0) ) + fUseSimplifiedDylibReExports = true; return; } } @@ -581,20 +610,20 @@ bool Options::checkForFile(const char* format, const char* dir, const char* root { char possiblePath[strlen(dir)+strlen(rootName)+strlen(format)+8]; sprintf(possiblePath, format, dir, rootName); - bool found = result.checkFileExists(possiblePath); + bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), possiblePath); return found; } -Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) +Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) const { FileInfo result; const int rootNameLen = strlen(rootName); // if rootName ends in .o there is no .a vs .dylib choice if ( (rootNameLen > 3) && (strcmp(&rootName[rootNameLen-2], ".o") == 0) ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -603,19 +632,33 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) } } else { - bool lookForDylibs = ( fOutputKind != Options::kDyld); + bool lookForDylibs = false; + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kObjectFile: // + lookForDylibs = true; + break; + case Options::kStaticExecutable: + case Options::kDyld: + case Options::kPreload: + case Options::kKextBundle: + lookForDylibs = false; + break; + } switch ( fLibrarySearchMode ) { case kSearchAllDirsForDylibsThenAllDirsForArchives: // first look in all directories for just for dylibs if ( lookForDylibs ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; } - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -625,7 +668,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) } // next look in all directories for just for archives if ( !dylibsOnly ) { - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -637,7 +680,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) case kSearchDylibAndArchiveInEachDir: // look in each directory for just for a dylib then for an archive - for (std::vector::iterator it = fLibrarySearchPaths.begin(); + for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; @@ -654,7 +697,7 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) throwf("library not found for -l%s", rootName); } -Options::FileInfo Options::findFramework(const char* frameworkName) +Options::FileInfo Options::findFramework(const char* frameworkName) const { if ( frameworkName == NULL ) throw "-framework missing next argument"; @@ -670,9 +713,9 @@ Options::FileInfo Options::findFramework(const char* frameworkName) return findFramework(name, suffix); } -Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) +Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const { - for (std::vector::iterator it = fFrameworkSearchPaths.begin(); + for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); it != fFrameworkSearchPaths.end(); it++) { // ??? Shouldn't we be using String here and just initializing it? @@ -693,7 +736,7 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi } } FileInfo result; - bool found = result.checkFileExists(possiblePath); + bool found = result.checkFileExists(*this, possiblePath); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound framework: '%s'\n", (found ? " " : " not "), possiblePath); @@ -724,13 +767,13 @@ Options::FileInfo Options::findFile(const char* path) const if ( possiblePath[sdkPathDirLen-1] == '/' ) possiblePath[sdkPathDirLen-1] = '\0'; strcat(possiblePath, path); - if ( result.checkFileExists(possiblePath) ) { + if ( result.checkFileExists(*this, possiblePath) ) { return result; } } } // try raw path - if ( result.checkFileExists(path) ) { + if ( result.checkFileExists(*this, path) ) { return result; } @@ -743,7 +786,7 @@ Options::FileInfo Options::findFile(const char* path) const strcpy(&addPoint[1], &path[17]); else strcpy(newPath, &path[17]); - if ( result.checkFileExists(newPath) ) { + if ( result.checkFileExists(*this, newPath) ) { return result; } } @@ -815,7 +858,8 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) const // If we didn't find it fall back to findFile. return findFile(path); } - + + void Options::parseSegAddrTable(const char* segAddrPath, const char* installPth) { @@ -886,12 +930,16 @@ void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdina file = fopen(realFileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, realFileOfPaths); } } else { file = fopen(fileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depFileList, fileOfPaths); } char path[PATH_MAX]; @@ -1064,6 +1112,9 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, SetW if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read %s file: %s", option, fileOfExports); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfExports); + ::close(fd); // parse into symbols and add to unordered_set @@ -1135,6 +1186,8 @@ void Options::parseAliasFile(const char* fileOfAliases) throwf("can't read alias file: %s", fileOfAliases); p[stat_buf.st_size] = '\n'; ::close(fd); + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, fileOfAliases); // parse into symbols and add to fAliases AliasPair pair; @@ -1248,7 +1301,14 @@ void Options::setMacOSXVersionMin(const char* version) throw "-macosx_version_min argument missing"; if ( (strncmp(version, "10.", 3) == 0) && isdigit(version[3]) ) { - unsigned int minorVersion = version[3] - '0'; + unsigned int minorVersion = 0; + for (int i=3; isdigit(version[i]); ++i) { + minorVersion = minorVersion*10 + (version[i] - '0'); + } + if ( minorVersion > 255 ) { + warning("Mac OS X minor version > 255 in '%s'", version); + minorVersion = 255; + } fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); } else { @@ -1533,6 +1593,8 @@ void Options::parseOrderFile(const char* path, bool cstring) throwf("can't read order file: %s", path); ::close(fd); p[stat_buf.st_size] = '\n'; + if ( this->dumpDependencyInfo() ) + this->dumpDependency(Options::depMisc, path); // parse into vector of pairs char * const end = &p[stat_buf.st_size+1]; @@ -1592,6 +1654,14 @@ void Options::parseOrderFile(const char* path, bool cstring) objFileName = symbolStart; symbolStart = &colon[3]; } + else { + colon = strstr(symbolStart, ".o):"); + if ( colon != NULL ) { + colon[3] = '\0'; + objFileName = symbolStart; + symbolStart = &colon[4]; + } + } // trim leading spaces while ( isspace(*symbolStart) ) ++symbolStart; @@ -1662,6 +1732,27 @@ void Options::addSection(const char* segment, const char* section, const char* p fExtraSections.push_back(info); } +void Options::addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection) +{ + if ( strlen(srcSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(srcSection) > 16 ) + throw "-rename_section section name max 16 chars"; + if ( strlen(dstSegment) > 16 ) + throw "-rename_section segment name max 16 chars"; + if ( strlen(dstSection) > 16 ) + throw "-rename_section section name max 16 chars"; + + SectionRename info; + info.fromSegment = srcSegment; + info.fromSection = srcSection; + info.toSegment = dstSegment; + info.toSection = dstSection; + + fSectionRenames.push_back(info); +} + + void Options::addSectionAlignment(const char* segment, const char* section, const char* alignmentStr) { if ( strlen(segment) > 16 ) @@ -2264,13 +2355,34 @@ void Options::parse(int argc, const char* argv[]) } // Use this flag to set default behavior for deployement targets. else if ( strcmp(arg, "-macosx_version_min") == 0 ) { - setMacOSXVersionMin(argv[++i]); + const char* macVers = argv[++i]; + const char* envMacVers = getenv("MACOSX_DEPLOYMENT_TARGET"); + const char* enviPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + if ( (envMacVers != NULL) && (enviPhoneVers != NULL) ) { + // when conflicting deployments set, break tie by looking at syslibroot + warning("both MACOSX_DEPLOYMENT_TARGET and IPHONEOS_DEPLOYMENT_TARGET are set"); + if ( !fSDKPaths.empty() ) { + const char* sysrootPath = fSDKPaths.back(); + const char* lastSlash = strrchr(sysrootPath, '/'); + if ( strstr(lastSlash, "Simulator") != NULL ) + setIOSVersionMin(enviPhoneVers); + else + setMacOSXVersionMin(macVers); + } + else { + setMacOSXVersionMin(macVers); + } + } + else { + setMacOSXVersionMin(macVers); + } } else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) { setIOSVersionMin(argv[++i]); } else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) { setIOSVersionMin(argv[++i]); + fTargetIOSSimulator = true; } else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); @@ -2495,18 +2607,6 @@ void Options::parse(int argc, const char* argv[]) snapshotFileArgIndex = 1; parseAliasFile(argv[++i]); } - // put this last so that it does not interfer with other options starting with 'i' - else if ( strncmp(arg, "-i", 2) == 0 ) { - const char* colon = strchr(arg, ':'); - if ( colon == NULL ) - throwf("unknown option: %s", arg); - Options::AliasPair pair; - char* temp = new char[colon-arg]; - strlcpy(temp, &arg[2], colon-arg-1); - pair.realName = &colon[1]; - pair.alias = temp; - fAliases.push_back(pair); - } else if ( strcmp(arg, "-save-temps") == 0 ) { fSaveTempFiles = true; } @@ -2603,6 +2703,12 @@ void Options::parse(int argc, const char* argv[]) throw "missing argument to -mllvm"; fLLVMOptions.push_back(opts); } + else if ( strcmp(arg, "-mcpu") == 0 ) { + const char* cpu = argv[++i]; + if ( cpu == NULL ) + throw "missing argument to -mcpu"; + fLtoCpu = cpu; + } else if ( strcmp(arg, "-no_order_inits") == 0 ) { fAutoOrderInitializers = false; } @@ -2756,6 +2862,14 @@ void Options::parse(int argc, const char* argv[]) fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); fSnapshotRequested = true; } + else if (strcmp(arg, "-snapshot_dir") == 0) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-snapshot_dir missing path"; + fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); + fLinkSnapshot.setSnapshotPath(path); + fSnapshotRequested = true; + } else if ( strcmp(arg, "-new_main") == 0 ) { fEntryPointLoadCommandForceOn = true; } @@ -2789,6 +2903,80 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { fKextsUseStubs = true; } + else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + ++i; + // previously handled by buildSearchPaths() + } + else if ( strcmp(arg, "-export_dynamic") == 0 ) { + fExportDynamic = true; + } + else if ( strcmp(arg, "-force_symbols_coalesce_list") == 0 ) { + snapshotFileArgIndex = 1; + loadExportFile(argv[++i], "-force_symbols_coalesce_list", fForceCoalesceSymbols); + } + else if ( strcmp(arg, "-add_linker_option") == 0 ) { + // ex: -add_linker_option '-framework Foundation' + const char* optString = argv[++i]; + if ( optString == NULL ) + throw "-add_linker_option missing

"; + addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]); + i += 4; + } + else if ( strcmp(arg, "-no_branch_islands") == 0 ) { + fAllowBranchIslands = false; + } + // put this last so that it does not interfer with other options starting with 'i' + else if ( strncmp(arg, "-i", 2) == 0 ) { + const char* colon = strchr(arg, ':'); + if ( colon == NULL ) + throwf("unknown option: %s", arg); + Options::AliasPair pair; + char* temp = new char[colon-arg]; + strlcpy(temp, &arg[2], colon-arg-1); + pair.realName = &colon[1]; + pair.alias = temp; + fAliases.push_back(pair); + } else { throwf("unknown option: %s", arg); } @@ -2918,6 +3106,12 @@ void Options::buildSearchPaths(int argc, const char* argv[]) else if ( strcmp(argv[i], "-fatal_warnings") == 0 ) { sFatalWarnings = true; } + else if ( strcmp(argv[i], "-dependency_info") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + throw "-dependency_info missing "; + fDependencyInfoPath = path; + } } int standardLibraryPathsStartIndex = libraryPaths.size(); int standardFrameworkPathsStartIndex = frameworkPaths.size(); @@ -3198,17 +3392,14 @@ void Options::reconfigureDefaults() #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); - #elif defined(DEFAULT_MACOSX_MIN_VERSION) - warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); - setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else - warning("-macosx_version_min not specified, assuming 10.6"); - fMacVersionMin = ld::mac10_6; + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); #endif } break; default: - // architecture will be infered ;ater by examining .o files + // architecture will be infered later by examining .o files break; } } @@ -3229,6 +3420,12 @@ void Options::reconfigureDefaults() fMacVersionMin = ld::mac10_4; } break; + case CPU_TYPE_ARM64: + if ( fIOSVersionMin < ld::iOS_7_0 ) { + //warning("-mios_version_min should be 7.0 or later for arm64"); + fIOSVersionMin = ld::iOS_7_0; + } + break; } // default to adding functions start for dynamic code, static code must opt-in @@ -3242,6 +3439,11 @@ void Options::reconfigureDefaults() fFunctionStartsLoadCommand = true; break; case Options::kObjectFile: + if ( !fDataInCodeInfoLoadCommandForcedOff ) + fDataInCodeInfoLoadCommand = true; + if ( fFunctionStartsForcedOn ) + fFunctionStartsLoadCommand = true; + break; case Options::kDynamicExecutable: case Options::kDyld: case Options::kDynamicLibrary: @@ -3263,6 +3465,14 @@ void Options::reconfigureDefaults() fAllowTextRelocs = true; fUndefinedTreatment = kUndefinedDynamicLookup; break; + case CPU_TYPE_ARM64: + // arm64 uses new MH_KEXT_BUNDLE type + fMakeCompressedDyldInfo = false; + fMakeCompressedDyldInfoForceOff = true; + fAllowTextRelocs = false; + fKextsUseStubs = true; + fUndefinedTreatment = kUndefinedDynamicLookup; + break; case CPU_TYPE_ARM: if ( fIOSVersionMin >= ld::iOS_5_0 ) { // iOS 5.0 and later use new MH_KEXT_BUNDLE type @@ -3470,11 +3680,16 @@ void Options::reconfigureDefaults() // -r -x implies -S if ( (fOutputKind == Options::kObjectFile) && (fLocalSymbolHandling == kLocalSymbolsNone) ) fDebugInfoStripping = Options::kDebugInfoNone; - + + // -r implies -no_uuid + if ( fOutputKind == Options::kObjectFile ) + fUUIDMode = kUUIDNone; + // choose how to process unwind info switch ( fArchitecture ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: switch ( fOutputKind ) { case Options::kObjectFile: case Options::kStaticExecutable: @@ -3501,10 +3716,10 @@ void Options::reconfigureDefaults() break; } - // only ARM main executables can be encrypted + // only iOS main executables should be encrypted if ( fOutputKind != Options::kDynamicExecutable ) fEncryptable = false; - if ( fArchitecture != CPU_TYPE_ARM ) + if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) ) fEncryptable = false; // don't move inits in dyld because dyld wants certain @@ -3554,10 +3769,17 @@ void Options::reconfigureDefaults() fMakeCompressedDyldInfo = false; } - - // only ARM enforces that cpu-sub-types must match - if ( fArchitecture != CPU_TYPE_ARM ) - fAllowCpuSubtypeMismatches = true; + // only ARM and x86_64 enforces that cpu-sub-types must match + switch ( fArchitecture ) { + case CPU_TYPE_ARM: + case CPU_TYPE_X86_64: + break; + case CPU_TYPE_I386: + case CPU_TYPE_ARM64: + fAllowCpuSubtypeMismatches = true; + break; + } + // only final linked images can not optimize zero fill sections if ( fOutputKind == Options::kObjectFile ) @@ -3611,6 +3833,11 @@ void Options::reconfigureDefaults() if ( fDisablePositionIndependentExecutable ) fPositionIndependentExecutable = false; + // arm64 is always PIE + if ( (fArchitecture == CPU_TYPE_ARM64) && (fOutputKind == kDynamicExecutable) ) { + fPositionIndependentExecutable = true; + } + // set fOutputSlidable switch ( fOutputKind ) { case Options::kObjectFile: @@ -3635,7 +3862,10 @@ void Options::reconfigureDefaults() if ( fMacVersionMin >= ld::mac10_7 ) { fTLVSupport = true; } - + else if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= 0x00080000) ) { + fTLVSupport = true; + } + // default to adding version load command for dynamic code, static code must opt-in switch ( fOutputKind ) { case Options::kObjectFile: @@ -3653,13 +3883,6 @@ void Options::reconfigureDefaults() case Options::kDynamicBundle: if ( !fVersionLoadCommandForcedOff ) fVersionLoadCommand = true; - // for now, don't create version load commands for iOS simulator builds - if ( fVersionLoadCommand && (fArchitecture == CPU_TYPE_I386) ) { - for (std::vector::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) { - if ( strstr(*sdkit, "/iPhoneSimulator.platform/") != NULL ) - fVersionLoadCommand = false; - } - } break; } @@ -3699,12 +3922,7 @@ void Options::reconfigureDefaults() fNeedsThreadLoadCommand = true; } else { - if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fArchitecture == CPU_TYPE_I386) ) { - // don't use LC_MAIN for simulator until min host OS is 10.8 for simulator - fNeedsThreadLoadCommand = true; - fEntryPointLoadCommand = false; - } - else if ( minOS(ld::mac10_8, ld::iOS_6_0) ) { + if ( minOS(ld::mac10_8, ld::iOS_6_0) ) { fEntryPointLoadCommand = true; fEntryName = "_main"; } @@ -3816,6 +4034,70 @@ void Options::reconfigureDefaults() } } + // allow trie based absolute symbols if targeting new enough OS + if ( fMakeCompressedDyldInfo ) { + if ( minOS(ld::mac10_9, ld::iOS_7_0) ) { + // Allow absolute symbols in export trie for device but not simulator + if ( !fTargetIOSSimulator ) + fAbsoluteSymbols = true; + } + } + + // iOS main executables now default to 16KB page size + if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fOutputKind == Options::kDynamicExecutable) ) { + // Only third party apps should have 16KB page segments by default + if ( fEncryptable ) { + if ( fSegmentAlignment == 4096 ) + fSegmentAlignment = 4096*4; + } + } + + // ARM64 needs 16KB page size for user land code + if ( fArchitecture == CPU_TYPE_ARM64 ) { + if ( fSegmentAlignment == 4096 ) { + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + fSegmentAlignment = 4096*4; + break; + case Options::kStaticExecutable: + case Options::kKextBundle: + case Options::kObjectFile: + case Options::kPreload: + break; + } + } + } + + // linker should not convert dwarf unwind if .o file has compact unwind section + switch ( fOutputKind ) { + case Options::kDynamicExecutable: + case Options::kDynamicLibrary: + case Options::kDynamicBundle: + case Options::kDyld: + if ( fKeepDwarfUnwindForcedOn ) { + fKeepDwarfUnwind = true; + } + else if ( fKeepDwarfUnwindForcedOff ) { + fKeepDwarfUnwind = false; + } + else { + if ( minOS(ld::mac10_9, ld::iOS_7_0) ) + fKeepDwarfUnwind = false; + else + fKeepDwarfUnwind = true; + } + break; + case Options::kKextBundle: + case Options::kStaticExecutable: + case Options::kObjectFile: + case Options::kPreload: + fKeepDwarfUnwind = true; + break; + } + } void Options::checkIllegalOptionCombinations() @@ -3890,6 +4172,7 @@ void Options::checkIllegalOptionCombinations() throw "-stack_addr must be < 4G for 32-bit processes"; break; case CPU_TYPE_X86_64: + case CPU_TYPE_ARM64: break; } if ( (fStackAddr & -4096) != fStackAddr ) @@ -3917,11 +4200,19 @@ void Options::checkIllegalOptionCombinations() fStackAddr = 0x2F000000; if ( fStackAddr > 0x30000000) throw "-stack_addr must be < 0x30000000 for arm"; + break; case CPU_TYPE_X86_64: if ( fStackAddr == 0 ) { fStackAddr = 0x00007FFF5C000000LL; } break; + case CPU_TYPE_ARM64: + if ( fStackSize > 0x20000000 ) + throw "-stack_size must be < 512MB"; + if ( fStackAddr == 0 ) { + fStackAddr = 0x120000000; + } + break; } if ( (fStackSize & -4096) != fStackSize ) throw "-stack_size must be multiples of 4K"; @@ -4018,6 +4309,10 @@ void Options::checkIllegalOptionCombinations() if ( fSetuidSafe && (fOutputKind == Options::kObjectFile) ) throw "-setuid_safe cannot be used with -r"; + // compiler driver no longer uses -objc_abi_version, it uses -ios_simulator_version_min instead + if ( !fObjCABIVersion1Override && !fObjCABIVersion2Override && fTargetIOSSimulator ) + fObjCABIVersion2Override = true; + // rdar://problem/4718189 map ObjC class names to new runtime names bool alterObjC1ClassNamesToObjC2 = false; switch (fArchitecture) { @@ -4028,6 +4323,7 @@ void Options::checkIllegalOptionCombinations() break; case CPU_TYPE_X86_64: case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: alterObjC1ClassNamesToObjC2 = true; break; } @@ -4123,6 +4419,7 @@ void Options::checkIllegalOptionCombinations() // first 4KB for 32-bit architectures fZeroPageSize = 0x1000; break; + case CPU_TYPE_ARM64: case CPU_TYPE_X86_64: // first 4GB for x86_64 on all OS's fZeroPageSize = 0x100000000ULL; @@ -4240,6 +4537,10 @@ void Options::checkIllegalOptionCombinations() // -dyld_env can only be used with main executables if ( (fOutputKind != Options::kDynamicExecutable) && (fDyldEnvironExtras.size() != 0) ) throw "-dyld_env can only used used when created main executables"; + + // -rename_sections can only be used in -r mode + if ( (fSectionRenames.size() != 0) && (fOutputKind != Options::kObjectFile) ) + throw "-rename_sections can only used used in -r mode"; } @@ -4392,3 +4693,41 @@ const char* Options::demangleSymbol(const char* sym) const return sym; } + +void Options::dumpDependency(uint8_t opcode, const char* path) const +{ + if ( !this->dumpDependencyInfo() ) + return; + + // one time open() of -dependency_info file + if ( fDependencyFileDescriptor == -1 ) { + fDependencyFileDescriptor = open(this->dependencyInfoPath(), O_WRONLY | O_TRUNC | O_CREAT, 0666); + if ( fDependencyFileDescriptor == -1 ) + throwf("Could not open or create -dependency_info file: %s", this->dependencyInfoPath()); + + // write header + uint8_t version = depLinkerVersion; + if ( write(fDependencyFileDescriptor, &version, 1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + extern const char ldVersionString[]; + if ( write(fDependencyFileDescriptor, ldVersionString, strlen(ldVersionString)+1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + } + + char realPath[PATH_MAX]; + if ( path[0] != '/' ) { + if ( realpath(path, realPath) != NULL ) { + path = realPath; + } + } + + if ( write(fDependencyFileDescriptor, &opcode, 1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + if ( write(fDependencyFileDescriptor, path, strlen(path)+1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + + //fprintf(stderr, "0x%02X %s\n", opcode, path); +} + + + diff --git a/ld64/src/ld/Options.h b/ld64/src/ld/Options.h index 41e5653..0195815 100644 --- a/ld64/src/ld/Options.h +++ b/ld64/src/ld/Options.h @@ -45,13 +45,15 @@ class LibraryOptions { public: LibraryOptions() : fWeakImport(false), fReExport(false), fBundleLoader(false), - fLazyLoad(false), fUpward(false), fForceLoad(false) {} + fLazyLoad(false), fUpward(false), fIndirectDylib(false), + fForceLoad(false) {} // for dynamic libraries bool fWeakImport; bool fReExport; bool fBundleLoader; bool fLazyLoad; bool fUpward; + bool fIndirectDylib; // for static libraries bool fForceLoad; }; @@ -115,12 +117,12 @@ public: // If the object already has a path the p must be NULL. // If the object does not have a path then p can be any candidate path, and if the file exists the object permanently remembers the path. // Returns true if the file exists, false if not. - bool checkFileExists(const char *p=NULL); + bool checkFileExists(const Options& options, const char *p=NULL); // Returns true if a previous call to checkFileExists() succeeded. // Returns false if the file does not exist of checkFileExists() has never been called. bool missing() const { return modTime==0; } -}; + }; struct ExtraSection { const char* segmentName; @@ -170,6 +172,21 @@ public: const char* alias; }; + struct SectionRename { + const char* fromSegment; + const char* fromSection; + const char* toSegment; + const char* toSection; + }; + + + enum { depLinkerVersion=0x00, depObjectFile=0x10, depDirectDylib=0x10, depIndirectDylib=0x10, + depUpwardDirectDylib=0x10, depUpwardIndirectDylib=0x10, depArchive=0x10, + depFileList=0x10, depSection=0x10, depBundleLoader=0x10, depMisc=0x10, depNotFound=0x11, + depOutputFile = 0x40 }; + + void dumpDependency(uint8_t, const char* path) const; + typedef const char* const* UndefinesIterator; // const ObjectFile::ReaderOptions& readerOptions(); @@ -322,25 +339,42 @@ public: bool canReExportSymbols() const { return fCanReExportSymbols; } const char* tempLtoObjectPath() const { return fTempLtoObjectPath; } const char* overridePathlibLTO() const { return fOverridePathlibLTO; } + const char* mcpuLTO() const { return fLtoCpu; } bool objcCategoryMerging() const { return fObjcCategoryMerging; } bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; } + bool keepDwarfUnwind() const { return fKeepDwarfUnwind; } + bool verboseOptimizationHints() const { return fVerboseOptimizationHints; } + bool ignoreOptimizationHints() const { return fIgnoreOptimizationHints; } + bool generateDtraceDOF() const { return fGenerateDtraceDOF; } + bool allowBranchIslands() const { return fAllowBranchIslands; } bool hasWeakBitTweaks() const; bool forceWeak(const char* symbolName) const; bool forceNotWeak(const char* symbolName) const; bool forceWeakNonWildCard(const char* symbolName) const; bool forceNotWeakNonWildcard(const char* symbolName) const; + bool forceCoalesce(const char* symbolName) const; Snapshot& snapshot() const { return fLinkSnapshot; } bool errorBecauseOfWarnings() const; bool needsThreadLoadCommand() const { return fNeedsThreadLoadCommand; } bool needsEntryPointLoadCommand() const { return fEntryPointLoadCommand; } bool needsSourceVersionLoadCommand() const { return fSourceVersionLoadCommand; } bool needsDependentDRInfo() const { return fDependentDRInfo; } + bool canUseAbsoluteSymbols() const { return fAbsoluteSymbols; } + bool allowSimulatorToLinkWithMacOSX() const { return fAllowSimulatorToLinkWithMacOSX; } uint64_t sourceVersion() const { return fSourceVersion; } uint32_t sdkVersion() const { return fSDKVersion; } const char* demangleSymbol(const char* sym) const; bool pipelineEnabled() const { return fPipelineFifo != NULL; } const char* pipelineFifo() const { return fPipelineFifo; } - + bool dumpDependencyInfo() const { return (fDependencyInfoPath != NULL); } + const char* dependencyInfoPath() const { return fDependencyInfoPath; } + bool targetIOSSimulator() const { return fTargetIOSSimulator; } + ld::relocatable::File::LinkerOptionsList& + linkerOptions() const { return fLinkerOptions; } + FileInfo findFramework(const char* frameworkName) const; + FileInfo findLibrary(const char* rootName, bool dylibsOnly=false) const; + const std::vector& sectionRenames() const { return fSectionRenames; } + private: typedef std::unordered_map NameToOrder; typedef std::unordered_set NameSet; @@ -372,9 +406,7 @@ private: void checkIllegalOptionCombinations(); void buildSearchPaths(int argc, const char* argv[]); void parseArch(const char* architecture); - FileInfo findLibrary(const char* rootName, bool dylibsOnly=false); - FileInfo findFramework(const char* frameworkName); - FileInfo findFramework(const char* rootName, const char* suffix); + FileInfo findFramework(const char* rootName, const char* suffix) const; bool checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const; uint64_t parseVersionNumber64(const char*); @@ -404,6 +436,7 @@ private: void warnObsolete(const char* arg); uint32_t parseProtection(const char* prot); void loadSymbolOrderFile(const char* fileOfExports, NameToOrder& orderMapping); + void addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection); @@ -441,6 +474,7 @@ private: SetWithWildcards fForceWeakSymbols; SetWithWildcards fForceNotWeakSymbols; SetWithWildcards fReExportSymbols; + SetWithWildcards fForceCoalesceSymbols; NameSet fRemovedExports; NameToOrder fExportSymbolsOrder; ExportMode fExportMode; @@ -464,6 +498,7 @@ private: const char* fDyldInstallPath; const char* fTempLtoObjectPath; const char* fOverridePathlibLTO; + const char* fLtoCpu; uint64_t fZeroPageSize; uint64_t fStackSize; uint64_t fStackAddr; @@ -562,6 +597,17 @@ private: bool fDependentDRInfo; bool fDependentDRInfoForcedOn; bool fDependentDRInfoForcedOff; + bool fTargetIOSSimulator; + bool fExportDynamic; + bool fAbsoluteSymbols; + bool fAllowSimulatorToLinkWithMacOSX; + bool fKeepDwarfUnwind; + bool fKeepDwarfUnwindForcedOn; + bool fKeepDwarfUnwindForcedOff; + bool fVerboseOptimizationHints; + bool fIgnoreOptimizationHints; + bool fGenerateDtraceDOF; + bool fAllowBranchIslands; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; ld::MacVersionMin fMacVersionMin; @@ -582,10 +628,14 @@ private: std::vector fFrameworkSearchPaths; std::vector fSDKPaths; std::vector fDyldEnvironExtras; + std::vector< std::vector > fLinkerOptions; + std::vector fSectionRenames; bool fSaveTempFiles; - mutable Snapshot fLinkSnapshot; - bool fSnapshotRequested; - const char * fPipelineFifo; + mutable Snapshot fLinkSnapshot; + bool fSnapshotRequested; + const char* fPipelineFifo; + const char* fDependencyInfoPath; + mutable int fDependencyFileDescriptor; }; diff --git a/ld64/src/ld/OutputFile.cpp b/ld64/src/ld/OutputFile.cpp index 16fdd66..c7a0918 100644 --- a/ld64/src/ld/OutputFile.cpp +++ b/ld64/src/ld/OutputFile.cpp @@ -69,16 +69,20 @@ namespace ld { namespace tool { +uint32_t sAdrpNA = 0; +uint32_t sAdrpNoped = 0; +uint32_t sAdrpNotNoped = 0; + OutputFile::OutputFile(const Options& opts) : - hasWeakExternalSymbols(false), usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), - _noReExportedDylibs(false), hasThreadLocalVariableDefinitions(false), pieDisabled(false), hasDataInCode(false), + usesWeakExternalSymbols(false), overridesWeakExternalSymbols(false), + _noReExportedDylibs(false), pieDisabled(false), hasDataInCode(false), headerAndLoadCommandsSection(NULL), rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), lazyBindingSection(NULL), exportSection(NULL), splitSegInfoSection(NULL), functionStartsSection(NULL), - dataInCodeSection(NULL), dependentDRsSection(NULL), + dataInCodeSection(NULL), optimizationHintsSection(NULL), dependentDRsSection(NULL), symbolTableSection(NULL), stringPoolSection(NULL), localRelocationsSection(NULL), externalRelocationsSection(NULL), sectionRelocationsSection(NULL), @@ -94,6 +98,7 @@ OutputFile::OutputFile(const Options& opts) _hasDynamicSymbolTable(true), _hasLocalRelocations(!opts.makeCompressedDyldInfo()), _hasExternalRelocations(!opts.makeCompressedDyldInfo()), + _hasOptimizationHints(opts.outputKind() == Options::kObjectFile), _encryptedTEXTstartOffset(0), _encryptedTEXTendOffset(0), _localSymbolsStartIndex(0), @@ -115,7 +120,8 @@ OutputFile::OutputFile(const Options& opts) _splitSegInfoAtom(NULL), _functionStartsAtom(NULL), _dataInCodeAtom(NULL), - _dependentDRInfoAtom(NULL) + _dependentDRInfoAtom(NULL), + _optimizationHintsAtom(NULL) { } @@ -143,9 +149,9 @@ void OutputFile::write(ld::Internal& state) this->buildDylibOrdinalMapping(state); this->addLoadCommands(state); this->addLinkEdit(state); - this->setSectionSizesAndAlignments(state); + state.setSectionSizesAndAlignments(); this->setLoadCommandsPadding(state); - this->assignFileOffsets(state); + _fileSize = state.assignFileOffsets(); this->assignAtomAddresses(state); this->synthesizeDebugNotes(state); this->buildSymbolTable(state); @@ -192,7 +198,6 @@ void OutputFile::assignAtomAddresses(ld::Internal& state) if ( log ) fprintf(stderr, " section=%s/%s\n", sect->segmentName(), sect->sectionName()); for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; - if ( log ) fprintf(stderr, " atom=%p, name=%s\n", atom, atom->name()); switch ( sect-> type() ) { case ld::Section::typeImportProxies: // want finalAddress() of all proxy atoms to be zero @@ -207,6 +212,7 @@ void OutputFile::assignAtomAddresses(ld::Internal& state) break; default: (const_cast(atom))->setSectionStartAddress(sect->address); + if ( log ) fprintf(stderr, " atom=%p, addr=0x%08llX, name=%s\n", atom, atom->finalAddress(), atom->name()); break; } } @@ -255,6 +261,12 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _dataInCodeAtom->encode(); } + if ( _hasOptimizationHints ) { + // build linker-optimization-hint info + assert(_optimizationHintsAtom != NULL); + _optimizationHintsAtom->encode(); + } + if ( _options.needsDependentDRInfo() ) { // build dependent dylib DR info assert(_dependentDRInfoAtom != NULL); @@ -326,83 +338,6 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _fileSize = state.sections.back()->fileOffset + state.sections.back()->size; } -void OutputFile::setSectionSizesAndAlignments(ld::Internal& state) -{ - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { - // absolute symbols need their finalAddress() to their value - for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - (const_cast(atom))->setSectionOffset(atom->objectAddress()); - } - } - else { - uint16_t maxAlignment = 0; - uint64_t offset = 0; - for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { - const ld::Atom* atom = *ait; - bool pagePerAtom = false; - uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; - if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) { - switch ( atom->section().type() ) { - case ld::Section::typeUnclassified: - case ld::Section::typeTentativeDefs: - case ld::Section::typeZeroFill: - pagePerAtom = true; - if ( atomAlignmentPowerOf2 < 12 ) - atomAlignmentPowerOf2 = 12; - break; - default: - break; - } - } - if ( atomAlignmentPowerOf2 > maxAlignment ) - maxAlignment = atomAlignmentPowerOf2; - // calculate section offset for this atom - uint64_t alignment = 1 << atomAlignmentPowerOf2; - uint64_t currentModulus = (offset % alignment); - uint64_t requiredModulus = atom->alignment().modulus; - if ( currentModulus != requiredModulus ) { - if ( requiredModulus > currentModulus ) - offset += requiredModulus-currentModulus; - else - offset += requiredModulus+alignment-currentModulus; - } - // LINKEDIT atoms are laid out later - if ( sect->type() != ld::Section::typeLinkEdit ) { - (const_cast(atom))->setSectionOffset(offset); - offset += atom->size(); - if ( pagePerAtom ) { - offset = (offset + 4095) & (-4096); // round up to end of page - } - } - if ( (atom->scope() == ld::Atom::scopeGlobal) - && (atom->definition() == ld::Atom::definitionRegular) - && (atom->combine() == ld::Atom::combineByName) - && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) - || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { - this->hasWeakExternalSymbols = true; - if ( _options.warnWeakExports() ) - warning("weak external symbol: %s", atom->name()); - } - } - sect->size = offset; - // section alignment is that of a contained atom with the greatest alignment - sect->alignment = maxAlignment; - // unless -sectalign command line option overrides - if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) - sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); - // each atom in __eh_frame has zero alignment to assure they pack together, - // but compilers usually make the CFIs pointer sized, so we want whole section - // to start on pointer sized boundary. - if ( sect->type() == ld::Section::typeCFI ) - sect->alignment = 3; - if ( sect->type() == ld::Section::typeTLVDefs ) - this->hasThreadLocalVariableDefinitions = true; - } - } -} void OutputFile::setLoadCommandsPadding(ld::Internal& state) { @@ -427,13 +362,14 @@ void OutputFile::setLoadCommandsPadding(ld::Internal& state) default: // work backwards from end of segment and lay out sections so that extra room goes to padding atom uint64_t addr = 0; + uint64_t textSegPageSize = _options.segPageSize("__TEXT"); for (std::vector::reverse_iterator it = state.sections.rbegin(); it != state.sections.rend(); ++it) { ld::Internal::FinalSection* sect = *it; if ( strcmp(sect->segmentName(), "__TEXT") != 0 ) continue; if ( sect == headerAndLoadCommandsSection ) { addr -= headerAndLoadCommandsSection->size; - paddingSize = addr % _options.segmentAlignment(); + paddingSize = addr % textSegPageSize; break; } addr -= sect->size; @@ -484,192 +420,6 @@ uint64_t OutputFile::pageAlign(uint64_t addr, uint64_t pageSize) return ((addr+pageSize-1) & (-pageSize)); } - -void OutputFile::assignFileOffsets(ld::Internal& state) -{ - const bool log = false; - const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) - && (_options.outputKind() != Options::kPreload)); - const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); - - uint64_t address = 0; - const char* lastSegName = ""; - uint64_t floatingAddressStart = _options.baseAddress(); - - // first pass, assign addresses to sections in segments with fixed start addresses - if ( log ) fprintf(stderr, "Fixed address segments:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) - continue; - if ( segmentsArePageAligned ) { - if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { - address = _options.customSegmentAddress(sect->segmentName()); - lastSegName = sect->segmentName(); - } - } - // adjust section address based on alignment - uint64_t unalignedAddress = address; - uint64_t alignment = (1 << sect->alignment); - address = ( (unalignedAddress+alignment-1) & (-alignment) ); - - // update section info - sect->address = address; - sect->alignmentPaddingBytes = (address - unalignedAddress); - - // sanity check size - if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) - && (_options.outputKind() != Options::kStaticExecutable) ) - throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", - sect->sectionName(), address, sect->size); - - if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", - sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); - // update running totals - if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) - address += sect->size; - - // if TEXT segment address is fixed, then flow other segments after it - if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { - floatingAddressStart = address; - } - } - - // second pass, assign section address to sections in segments that are contiguous with previous segment - address = floatingAddressStart; - lastSegName = ""; - ld::Internal::FinalSection* overlappingFixedSection = NULL; - ld::Internal::FinalSection* overlappingFlowSection = NULL; - if ( log ) fprintf(stderr, "Regular layout segments:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) - continue; - if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { - sect->alignmentPaddingBytes = 0; - continue; - } - if ( segmentsArePageAligned ) { - if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { - // round up size of last segment if needed - if ( *lastSegName != '\0' ) { - address = pageAlign(address, _options.segPageSize(lastSegName)); - } - // set segment address based on end of last segment - address = pageAlign(address); - lastSegName = sect->segmentName(); - } - } - // adjust section address based on alignment - uint64_t unalignedAddress = address; - uint64_t alignment = (1 << sect->alignment); - address = ( (unalignedAddress+alignment-1) & (-alignment) ); - - // update section info - sect->address = address; - sect->alignmentPaddingBytes = (address - unalignedAddress); - - // sanity check size - if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) - && (_options.outputKind() != Options::kStaticExecutable) ) - throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", - sect->sectionName(), address, sect->size); - - // sanity check it does not overlap a fixed address segment - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* otherSect = *sit; - if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) - continue; - if ( sect->address > otherSect->address ) { - if ( (otherSect->address+otherSect->size) > sect->address ) { - overlappingFixedSection = otherSect; - overlappingFlowSection = sect; - } - } - else { - if ( (sect->address+sect->size) > otherSect->address ) { - overlappingFixedSection = otherSect; - overlappingFlowSection = sect; - } - } - } - - if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", - sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, - sect->segmentName(), sect->sectionName()); - // update running totals - if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) - address += sect->size; - } - if ( overlappingFixedSection != NULL ) { - fprintf(stderr, "Section layout:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( sect->isSectionHidden() ) - continue; - fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", - sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, - sect->segmentName(), sect->sectionName()); - - } - throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", - overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), - overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); - } - - - // third pass, assign section file offsets - uint64_t fileOffset = 0; - lastSegName = ""; - if ( log ) fprintf(stderr, "All segments with file offsets:\n"); - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( hasZeroForFileOffset(sect) ) { - // fileoff of zerofill sections is moot, but historically it is set to zero - sect->fileOffset = 0; - - // align file offset with address layout - fileOffset += sect->alignmentPaddingBytes; - } - else { - // page align file offset at start of each segment - if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { - fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); - } - lastSegName = sect->segmentName(); - - // align file offset with address layout - fileOffset += sect->alignmentPaddingBytes; - - // update section info - sect->fileOffset = fileOffset; - - // update running total - fileOffset += sect->size; - } - - if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", - sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, - sect->segmentName(), sect->sectionName()); - } - - - // for encrypted iPhoneOS apps - if ( _options.makeEncryptable() ) { - // remember end of __TEXT for later use by load command - for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { - ld::Internal::FinalSection* sect = *it; - if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { - _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); - } - } - } - - // remember total file size - _fileSize = fileOffset; -} - - static const char* makeName(const ld::Atom& atom) { static char buffer[4096]; @@ -923,49 +673,91 @@ void OutputFile::rangeCheckARM12(int64_t displacement, ld::Internal& state, cons } } +bool OutputFile::checkArmBranch24Displacement(int64_t displacement) +{ + return ( (displacement < 33554428LL) && (displacement > (-33554432LL)) ); +} void OutputFile::rangeCheckARMBranch24(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) { - if ( (displacement > 33554428LL) || (displacement < (-33554432LL)) ) { - // show layout of final image - printSectionLayout(state); + if ( checkArmBranch24Displacement(displacement) ) + return; - const ld::Atom* target; - throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); - } + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b/bl/blx ARM branch out of range (%lld max is +/-32MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); } -void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +bool OutputFile::checkThumbBranch22Displacement(int64_t displacement) { - // thumb2 supports a larger displacement + // thumb2 supports +/- 16MB displacement if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); + return false; } } else { + // thumb1 supports +/- 4MB displacement if ( (displacement > 4194302LL) || (displacement < (-4194304LL)) ) { - // show layout of final image - printSectionLayout(state); - - const ld::Atom* target; - throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)", - displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), - addressOf(state, fixup, &target)); + return false; } } + return true; +} + +void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + if ( checkThumbBranch22Displacement(displacement) ) + return; + + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) { + throwf("b/bl/blx thumb2 branch out of range (%lld max is +/-16MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } + else { + throwf("b/bl/blx thumb1 branch out of range (%lld max is +/-4MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } } +void OutputFile::rangeCheckARM64Branch26(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t bl_128MegLimit = 0x07FFFFFF; + if ( (displacement > bl_128MegLimit) || (displacement < (-bl_128MegLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("b(l) ARM64 branch out of range (%lld max is +/-128MB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} +void OutputFile::rangeCheckARM64Page21(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup) +{ + const int64_t adrp_4GigLimit = 0x100000000ULL; + if ( (displacement > adrp_4GigLimit) || (displacement < (-adrp_4GigLimit)) ) { + // show layout of final image + printSectionLayout(state); + + const ld::Atom* target; + throwf("ARM64 ADRP out of range (%lld max is +/-4GB): from %s (0x%08llX) to %s (0x%08llX)", + displacement, atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fixup), + addressOf(state, fixup, &target)); + } +} uint16_t OutputFile::get16LE(uint8_t* loc) { return LittleEndian::get16(*(uint16_t*)loc); } @@ -986,6 +778,517 @@ void OutputFile::set32BE(uint8_t* loc, uint32_t value) { BigEndian::set32(*( uint64_t OutputFile::get64BE(uint8_t* loc) { return BigEndian::get64(*(uint64_t*)loc); } void OutputFile::set64BE(uint8_t* loc, uint64_t value) { BigEndian::set64(*(uint64_t*)loc, value); } +static uint32_t makeNOP() { + return 0xD503201F; +} + +enum SignExtension { signedNot, signed32, signed64 }; +struct LoadStoreInfo { + uint32_t reg; + uint32_t baseReg; + uint32_t offset; // after scaling + uint32_t size; // 1,2,4,8, or 16 + bool isStore; + bool isFloat; // if destReg is FP/SIMD + SignExtension signEx; // if load is sign extended +}; + +static uint32_t makeLDR_literal(const LoadStoreInfo& info, uint64_t targetAddress, uint64_t instructionAddress) +{ + int64_t delta = targetAddress - instructionAddress; + assert(delta < 1024*1024); + assert(delta > -1024*1024); + assert((info.reg & 0xFFFFFFE0) == 0); + assert((targetAddress & 0x3) == 0); + assert((instructionAddress & 0x3) == 0); + assert(!info.isStore); + uint32_t imm19 = (delta << 3) & 0x00FFFFE0; + uint32_t instruction = 0; + switch ( info.size ) { + case 4: + if ( info.isFloat ) { + assert(info.signEx == signedNot); + instruction = 0x1C000000; + } + else { + if ( info.signEx == signed64 ) + instruction = 0x98000000; + else + instruction = 0x18000000; + } + break; + case 8: + assert(info.signEx == signedNot); + instruction = info.isFloat ? 0x5C000000 : 0x58000000; + break; + case 16: + assert(info.signEx == signedNot); + instruction = 0x9C000000; + break; + default: + assert(0 && "invalid load size for literal"); + } + return (instruction | imm19 | info.reg); +} + +static uint32_t makeADR(uint32_t destReg, uint64_t targetAddress, uint64_t instructionAddress) +{ + assert((destReg & 0xFFFFFFE0) == 0); + assert((instructionAddress & 0x3) == 0); + uint32_t instruction = 0x10000000; + int64_t delta = targetAddress - instructionAddress; + assert(delta < 1024*1024); + assert(delta > -1024*1024); + uint32_t immhi = (delta & 0x001FFFFC) << 3; + uint32_t immlo = (delta & 0x00000003) << 29; + return (instruction | immhi | immlo | destReg); +} + +static uint32_t makeLoadOrStore(const LoadStoreInfo& info) +{ + uint32_t instruction = 0x39000000; + if ( info.isFloat ) + instruction |= 0x04000000; + instruction |= info.reg; + instruction |= (info.baseReg << 5); + uint32_t sizeBits = 0; + uint32_t opcBits = 0; + uint32_t imm12Bits = 0; + switch ( info.size ) { + case 1: + sizeBits = 0; + imm12Bits = info.offset; + if ( info.isStore ) { + opcBits = 0; + } + else { + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + opcBits = 3; + break; + case signed64: + opcBits = 2; + break; + } + } + break; + case 2: + sizeBits = 1; + assert((info.offset % 2) == 0); + imm12Bits = info.offset/2; + if ( info.isStore ) { + opcBits = 0; + } + else { + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + opcBits = 3; + break; + case signed64: + opcBits = 2; + break; + } + } + break; + case 4: + sizeBits = 2; + assert((info.offset % 4) == 0); + imm12Bits = info.offset/4; + if ( info.isStore ) { + opcBits = 0; + } + else { + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + assert(0 && "cannot use signed32 with 32-bit load/store"); + break; + case signed64: + opcBits = 2; + break; + } + } + break; + case 8: + sizeBits = 3; + assert((info.offset % 8) == 0); + imm12Bits = info.offset/8; + if ( info.isStore ) { + opcBits = 0; + } + else { + opcBits = 1; + assert(info.signEx == signedNot); + } + break; + case 16: + sizeBits = 0; + assert((info.offset % 16) == 0); + imm12Bits = info.offset/16; + assert(info.isFloat); + if ( info.isStore ) { + opcBits = 2; + } + else { + opcBits = 3; + } + break; + default: + assert(0 && "bad load/store size"); + break; + } + assert(imm12Bits < 4096); + return (instruction | (sizeBits << 30) | (opcBits << 22) | (imm12Bits << 10)); +} + +static bool parseLoadOrStore(uint32_t instruction, LoadStoreInfo& info) +{ + if ( (instruction & 0x3B000000) != 0x39000000 ) + return false; + info.isFloat = ( (instruction & 0x04000000) != 0 ); + info.reg = (instruction & 0x1F); + info.baseReg = ((instruction>>5) & 0x1F); + switch (instruction & 0xC0C00000) { + case 0x00000000: + info.size = 1; + info.isStore = true; + info.signEx = signedNot; + break; + case 0x00400000: + info.size = 1; + info.isStore = false; + info.signEx = signedNot; + break; + case 0x00800000: + if ( info.isFloat ) { + info.size = 16; + info.isStore = true; + info.signEx = signedNot; + } + else { + info.size = 1; + info.isStore = false; + info.signEx = signed64; + } + break; + case 0x00C00000: + if ( info.isFloat ) { + info.size = 16; + info.isStore = false; + info.signEx = signedNot; + } + else { + info.size = 1; + info.isStore = false; + info.signEx = signed32; + } + break; + case 0x40000000: + info.size = 2; + info.isStore = true; + info.signEx = signedNot; + break; + case 0x40400000: + info.size = 2; + info.isStore = false; + info.signEx = signedNot; + break; + case 0x40800000: + info.size = 2; + info.isStore = false; + info.signEx = signed64; + break; + case 0x40C00000: + info.size = 2; + info.isStore = false; + info.signEx = signed32; + break; + case 0x80000000: + info.size = 4; + info.isStore = true; + info.signEx = signedNot; + break; + case 0x80400000: + info.size = 4; + info.isStore = false; + info.signEx = signedNot; + break; + case 0x80800000: + info.size = 4; + info.isStore = false; + info.signEx = signed64; + break; + case 0xC0000000: + info.size = 8; + info.isStore = true; + info.signEx = signedNot; + break; + case 0xC0400000: + info.size = 8; + info.isStore = false; + info.signEx = signedNot; + break; + default: + return false; + } + info.offset = ((instruction >> 10) & 0x0FFF) * info.size; + return true; +} + +struct AdrpInfo { + uint32_t destReg; +}; + +static bool parseADRP(uint32_t instruction, AdrpInfo& info) +{ + if ( (instruction & 0x9F000000) != 0x90000000 ) + return false; + info.destReg = (instruction & 0x1F); + return true; +} + +struct AddInfo { + uint32_t destReg; + uint32_t srcReg; + uint32_t addend; +}; + +static bool parseADD(uint32_t instruction, AddInfo& info) +{ + if ( (instruction & 0xFFC00000) != 0x91000000 ) + return false; + info.destReg = (instruction & 0x1F); + info.srcReg = ((instruction>>5) & 0x1F); + info.addend = ((instruction>>10) & 0xFFF); + return true; +} + + + +#if 0 +static uint32_t makeLDR_scaledOffset(const LoadStoreInfo& info) +{ + assert((info.reg & 0xFFFFFFE0) == 0); + assert((info.baseReg & 0xFFFFFFE0) == 0); + assert(!info.isFloat || (info.signEx != signedNot)); + uint32_t sizeBits = 0; + uint32_t opcBits = 1; + uint32_t vBit = info.isFloat; + switch ( info.signEx ) { + case signedNot: + opcBits = 1; + break; + case signed32: + opcBits = 3; + break; + case signed64: + opcBits = 2; + break; + default: + assert(0 && "bad SignExtension runtime value"); + } + switch ( info.size ) { + case 1: + sizeBits = 0; + break; + case 2: + sizeBits = 1; + break; + case 4: + sizeBits = 2; + break; + case 8: + sizeBits = 3; + break; + case 16: + sizeBits = 0; + vBit = 1; + opcBits = 3; + break; + default: + assert(0 && "invalid load size for literal"); + } + assert((info.offset % info.size) == 0); + uint32_t scaledOffset = info.offset/info.size; + assert(scaledOffset < 4096); + return (0x39000000 | (sizeBits<<30) | (vBit<<26) | (opcBits<<22) | (scaledOffset<<10) | (info.baseReg<<5) | info.reg); +} + +static uint32_t makeLDR_literal(uint32_t destReg, uint32_t loadSize, bool isFloat, uint64_t targetAddress, uint64_t instructionAddress) +{ + int64_t delta = targetAddress - instructionAddress; + assert(delta < 1024*1024); + assert(delta > -1024*1024); + assert((destReg & 0xFFFFFFE0) == 0); + assert((targetAddress & 0x3) == 0); + assert((instructionAddress & 0x3) == 0); + uint32_t imm19 = (delta << 3) & 0x00FFFFE0; + uint32_t instruction = 0; + switch ( loadSize ) { + case 4: + instruction = isFloat ? 0x1C000000 : 0x18000000; + break; + case 8: + instruction = isFloat ? 0x5C000000 : 0x58000000; + break; + case 16: + instruction = 0x9C000000; + break; + default: + assert(0 && "invalid load size for literal"); + } + return (instruction | imm19 | destReg); +} + + +static bool ldrInfo(uint32_t instruction, uint8_t* size, uint8_t* destReg, bool* v, uint32_t* scaledOffset) +{ + *v = ( (instruction & 0x04000000) != 0 ); + *destReg = (instruction & 0x1F); + uint32_t imm12 = ((instruction >> 10) & 0x00000FFF); + switch ( (instruction & 0xC0000000) >> 30 ) { + case 0: + // vector and byte LDR have same "size" bits, need to check other bits to differenciate + if ( (instruction & 0x00800000) == 0 ) { + *size = 1; + *scaledOffset = imm12; + } + else { + *size = 16; + *scaledOffset = imm12 * 16; + } + break; + case 1: + *size = 2; + *scaledOffset = imm12 * 2; + break; + case 2: + *size = 4; + *scaledOffset = imm12 * 4; + break; + case 3: + *size = 8; + *scaledOffset = imm12 * 8; + break; + } + return ((instruction & 0x3B400000) == 0x39400000); +} +#endif + +static bool withinOneMeg(uint64_t addr1, uint64_t addr2) { + int64_t delta = (addr2 - addr1); + return ( (delta < 1024*1024) && (delta > -1024*1024) ); +} + +void OutputFile::setInfo(ld::Internal& state, const ld::Atom* atom, uint8_t* buffer, const std::map& usedByHints, + uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info) +{ + info->offsetInAtom = offsetInAtom + delta; + std::map::const_iterator pos = usedByHints.find(info->offsetInAtom); + if ( (pos != usedByHints.end()) && (pos->second != NULL) ) { + info->fixup = pos->second; + info->targetAddress = addressOf(state, info->fixup, &info->target); + if ( info->fixup->clusterSize != ld::Fixup::k1of1 ) { + assert(info->fixup->firstInCluster()); + const ld::Fixup* nextFixup = info->fixup + 1; + if ( nextFixup->kind == ld::Fixup::kindAddAddend ) { + info->targetAddress += nextFixup->u.addend; + } + else { + assert(0 && "expected addend"); + } + } + } + else { + info->fixup = NULL; + info->targetAddress = 0; + info->target = NULL; + } + info->instructionContent = &buffer[info->offsetInAtom]; + info->instructionAddress = atom->finalAddress() + info->offsetInAtom; + info->instruction = get32LE(info->instructionContent); +} + +static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false) +{ + if ( fixup == NULL ) + return false; + const ld::Fixup* f; + switch ( fixup->kind ) { + case ld::Fixup::kindStoreTargetAddressARM64Page21: + return !mustBeGOT; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + return true; + case ld::Fixup::kindSetTargetAddress: + f = fixup; + do { + ++f; + } while ( ! f->lastInCluster() ); + switch (f->kind ) { + case ld::Fixup::kindStoreARM64Page21: + return !mustBeGOT; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + return true; + default: + break; + } + break; + default: + break; + } + return false; +} + +static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) +{ + if ( fixup == NULL ) + return false; + const ld::Fixup* f; + switch ( fixup->kind ) { + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + return !mustBeGOT; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + return true; + case ld::Fixup::kindSetTargetAddress: + f = fixup; + do { + ++f; + } while ( ! f->lastInCluster() ); + switch (f->kind ) { + case ld::Fixup::kindStoreARM64PageOff12: + return !mustBeGOT; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + return true; + default: + break; + } + break; + default: + break; + } + return false; +} + + +#define LOH_ASSERT(cond) \ + if ( !(cond) ) { \ + warning("ignoring linker optimzation hint at %s+0x%X because " #cond, atom->name(), fit->offsetInAtom); \ + break; \ + } + + void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer) { //fprintf(stderr, "applyFixUps() on %s\n", atom->name()); @@ -999,8 +1302,10 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: bool is_blx; bool is_b; bool thumbTarget = false; + std::map usedByHints; for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { uint8_t* fixUpLocation = &buffer[fit->offsetInAtom]; + ld::Fixup::LOH_arm64 lohExtra; switch ( (ld::Fixup::Kind)(fit->kind) ) { case ld::Fixup::kindNone: case ld::Fixup::kindNoneFollowOn: @@ -1023,16 +1328,18 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: accumulator -= delta; break; case ld::Fixup::kindAddAddend: - // ARM main executables main contain .long constants pointing - // into themselves such as jump tables. These .long should not have thumb bit set - // even though the target is a thumb instruction. We can tell it is an interior pointer - // because we are processing an addend. - if ( thumbTarget && (toTarget == atom) && ((int32_t)fit->u.addend > 0) ) { - accumulator &= (-2); - //warning("removing thumb bit from intra-atom pointer in %s %s+0x%0X", - // atom->section().sectionName(), atom->name(), fit->offsetInAtom); + if ( ! fit->contentIgnoresAddend ) { + // ARM main executables main contain .long constants pointing + // into themselves such as jump tables. These .long should not have thumb bit set + // even though the target is a thumb instruction. We can tell it is an interior pointer + // because we are processing an addend. + if ( thumbTarget && (toTarget == atom) && ((int32_t)fit->u.addend > 0) ) { + accumulator &= (-2); + //warning("removing thumb bit from intra-atom pointer in %s %s+0x%0X", + // atom->section().sectionName(), atom->name(), fit->offsetInAtom); + } + accumulator += fit->u.addend; } - accumulator += fit->u.addend; break; case ld::Fixup::kindSubtractAddend: accumulator -= fit->u.addend; @@ -1229,7 +1536,20 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, 0x46C04040); } break; + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to a NOP + set32LE(fixUpLocation, 0xD503201F); + } + break; + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + if ( _options.outputKind() != Options::kObjectFile ) { + // change call site to 'MOVZ X0,0' + set32LE(fixUpLocation, 0xD2800000); + } + break; case ld::Fixup::kindLazyTarget: + case ld::Fixup::kindIslandTarget: break; case ld::Fixup::kindSetLazyOffset: assert(fit->binding == ld::Fixup::bindingDirectlyBound); @@ -1242,6 +1562,17 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: case ld::Fixup::kindDataInCodeStartJTA32: case ld::Fixup::kindDataInCodeEnd: break; + case ld::Fixup::kindLinkerOptimizationHint: + // expand table of address/offsets used by hints + lohExtra.addend = fit->u.addend; + usedByHints[fit->offsetInAtom + (lohExtra.info.delta1 << 2)] = NULL; + if ( lohExtra.info.count > 0 ) + usedByHints[fit->offsetInAtom + (lohExtra.info.delta2 << 2)] = NULL; + if ( lohExtra.info.count > 1 ) + usedByHints[fit->offsetInAtom + (lohExtra.info.delta3 << 2)] = NULL; + if ( lohExtra.info.count > 2 ) + usedByHints[fit->offsetInAtom + (lohExtra.info.delta4 << 2)] = NULL; + break; case ld::Fixup::kindStoreTargetAddressLittleEndian32: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); @@ -1326,6 +1657,22 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: case ld::Fixup::kindStoreTargetAddressARMBranch24: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); + if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) { + // Branching to island. If ultimate target is in range, branch there directly. + for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); islandfit != end; ++islandfit) { + if ( islandfit->kind == ld::Fixup::kindIslandTarget ) { + const ld::Atom* islandTarget = NULL; + uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget); + delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4); + if ( checkArmBranch24Displacement(delta) ) { + toTarget = islandTarget; + accumulator = islandTargetAddress; + thumbTarget = targetIsThumb(state, islandfit); + } + break; + } + } + } if ( thumbTarget ) accumulator |= 1; if ( fit->contentDetlaToAddendOnly ) @@ -1336,18 +1683,18 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 8); rangeCheckARMBranch24(delta, state, atom, fit); instruction = get32LE(fixUpLocation); - // Make sure we are calling arm with bl, thumb with blx + // Make sure we are calling arm with bl, thumb with blx is_bl = ((instruction & 0xFF000000) == 0xEB000000); is_blx = ((instruction & 0xFE000000) == 0xFA000000); is_b = !is_blx && ((instruction & 0x0F000000) == 0x0A000000); - if ( is_bl && thumbTarget ) { - uint32_t opcode = 0xFA000000; + if ( (is_bl | is_blx) && thumbTarget ) { + uint32_t opcode = 0xFA000000; // force to be blx uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; uint32_t h_bit = (uint32_t)(delta << 23) & 0x01000000; newInstruction = opcode | h_bit | disp; } - else if ( is_blx && !thumbTarget ) { - uint32_t opcode = 0xEB000000; + else if ( (is_bl | is_blx) && !thumbTarget ) { + uint32_t opcode = 0xEB000000; // force to be bl uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF; newInstruction = opcode | disp; } @@ -1370,6 +1717,23 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: case ld::Fixup::kindStoreTargetAddressThumbBranch22: accumulator = addressOf(state, fit, &toTarget); thumbTarget = targetIsThumb(state, fit); + if ( toTarget->contentType() == ld::Atom::typeBranchIsland ) { + // branching to island, so see if ultimate target is in range + // and if so branch to ultimate target instead. + for (ld::Fixup::iterator islandfit = toTarget->fixupsBegin(), end=toTarget->fixupsEnd(); islandfit != end; ++islandfit) { + if ( islandfit->kind == ld::Fixup::kindIslandTarget ) { + const ld::Atom* islandTarget = NULL; + uint64_t islandTargetAddress = addressOf(state, islandfit, &islandTarget); + delta = islandTargetAddress - (atom->finalAddress() + fit->offsetInAtom + 4); + if ( checkThumbBranch22Displacement(delta) ) { + toTarget = islandTarget; + accumulator = islandTargetAddress; + thumbTarget = targetIsThumb(state, islandfit); + } + break; + } + } + } if ( thumbTarget ) accumulator |= 1; if ( fit->contentDetlaToAddendOnly ) @@ -1504,8 +1868,591 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(fixUpLocation, newInstruction); } break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64Branch26 case + case ld::Fixup::kindStoreARM64Branch26: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + rangeCheckARM64Branch26(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + newInstruction = (instruction & 0xFC000000) | ((uint32_t)(delta >> 2) & 0x03FFFFFF); + set32LE(fixUpLocation, newInstruction); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64Branch26 case + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: + case ld::Fixup::kindStoreARM64Page21: + { + // the ADRP instruction adds the imm << 12 to the page that the pc is on + if ( fit->contentAddendOnly ) + delta = 0; + else + delta = (accumulator & (-4096)) - ((atom->finalAddress() + fit->offsetInAtom) & (-4096)); + rangeCheckARM64Page21(delta, state, atom, fit); + instruction = get32LE(fixUpLocation); + uint32_t immhi = (delta >> 9) & (0x00FFFFE0); + uint32_t immlo = (delta << 17) & (0x60000000); + newInstruction = (instruction & 0x9F00001F) | immlo | immhi; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindAddressARM64PageOff12 case + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64PageOff12: + { + uint32_t offset = accumulator & 0x00000FFF; + instruction = get32LE(fixUpLocation); + // LDR/STR instruction have implicit scale factor, need to compensate for that + if ( instruction & 0x08000000 ) { + uint32_t implictShift = ((instruction >> 30) & 0x3); + switch ( implictShift ) { + case 0: + if ( (instruction & 0x04800000) == 0x04800000 ) { + // vector and byte LDR/STR have same "size" bits, need to check other bits to differenciate + implictShift = 4; + if ( (offset & 0xF) != 0 ) { + throwf("128-bit LDR/STR not 16-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + } + break; + case 1: + if ( (offset & 0x1) != 0 ) { + throwf("16-bit LDR/STR not 2-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + break; + case 2: + if ( (offset & 0x3) != 0 ) { + throwf("32-bit LDR/STR not 4-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + break; + case 3: + if ( (offset & 0x7) != 0 ) { + throwf("64-bit LDR/STR not 8-byte aligned: from %s (0x%08llX) to %s (0x%08llX)", + atom->name(), atom->finalAddress(), referenceTargetAtomName(state, fit), + addressOf(state, fit, &toTarget)); + } + break; + } + // compensate for implicit scale + offset >>= implictShift; + } + if ( fit->contentAddendOnly ) + offset = 0; + uint32_t imm12 = offset << 10; + newInstruction = (instruction & 0xFFC003FF) | imm12; + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64GOTLoadPage21 case + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + { + // GOT entry was optimized away, change LDR instruction to a ADD + instruction = get32LE(fixUpLocation); + if ( (instruction & 0xFFC00000) != 0xF9400000 ) + throwf("GOT load reloc does not point to a LDR instruction in %s", atom->name()); + uint32_t offset = accumulator & 0x00000FFF; + uint32_t imm12 = offset << 10; + newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: + accumulator = addressOf(state, fit, &toTarget); + // fall into kindStoreARM64TLVPLeaPageOff12 case + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: + { + // TLV thunk in same linkage unit, so LEA it directly, changing LDR instruction to a ADD + instruction = get32LE(fixUpLocation); + if ( (instruction & 0xFFC00000) != 0xF9400000 ) + throwf("TLV load reloc does not point to a LDR instruction in %s", atom->name()); + uint32_t offset = accumulator & 0x00000FFF; + uint32_t imm12 = offset << 10; + newInstruction = 0x91000000 | imm12 | (instruction & 0x000003FF); + set32LE(fixUpLocation, newInstruction); + } + break; + case ld::Fixup::kindStoreARM64PointerToGOT: + set64LE(fixUpLocation, accumulator); + break; + case ld::Fixup::kindStoreARM64PCRelToGOT: + if ( fit->contentAddendOnly ) + delta = accumulator; + else + delta = accumulator - (atom->finalAddress() + fit->offsetInAtom); + set32LE(fixUpLocation, delta); + break; +#endif + } + } + + // after all fixups are done on atom, if there are potential optimizations, do those + if ( (usedByHints.size() != 0) && (_options.outputKind() != Options::kObjectFile) && !_options.ignoreOptimizationHints() ) { + // fill in second part of usedByHints map, so we can see the target of fixups that might be optimized + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindLinkerOptimizationHint: + case ld::Fixup::kindNoneFollowOn: + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: + break; + default: + if ( fit->firstInCluster() ) { + std::map::iterator pos = usedByHints.find(fit->offsetInAtom); + if ( pos != usedByHints.end() ) { + assert(pos->second == NULL && "two fixups in same hint location"); + pos->second = fit; + //fprintf(stderr, "setting %s usedByHints[0x%04X], kind = %d\n", atom->name(), fit->offsetInAtom, fit->kind); + } + } + } + } + + // apply hints pass 1 + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint ) + continue; + InstructionInfo infoA; + InstructionInfo infoB; + InstructionInfo infoC; + InstructionInfo infoD; + LoadStoreInfo ldrInfoB, ldrInfoC; + AddInfo addInfoB; + AdrpInfo adrpInfoA; + bool usableSegment; + bool targetFourByteAligned; + bool literalableSize, isADRP, isADD, isLDR, isSTR; + //uint8_t loadSize, destReg; + //uint32_t scaledOffset; + //uint32_t imm12; + ld::Fixup::LOH_arm64 alt; + alt.addend = fit->u.addend; + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA); + if ( alt.info.count > 0 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB); + if ( alt.info.count > 1 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta3 << 2), &infoC); + if ( alt.info.count > 2 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta4 << 2), &infoD); + + switch ( alt.info.kind ) { + case LOH_ARM64_ADRP_ADRP: + // processed in pass 2 beacuse some ADRP may have been removed + break; + case LOH_ARM64_ADRP_LDR: + LOH_ASSERT(alt.info.count == 1); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + LOH_ASSERT(isLDR); + LOH_ASSERT(ldrInfoB.baseReg == adrpInfoA.destReg); + LOH_ASSERT(ldrInfoB.offset == (infoA.targetAddress & 0x00000FFF)); + literalableSize = ( (ldrInfoB.size != 1) && (ldrInfoB.size != 2) ); + targetFourByteAligned = ( (infoA.targetAddress & 0x3) == 0 ); + if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal\n", infoB.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr at 0x%08llX not transformed, isLDR=%d, literalableSize=%d, inRange=%d, usableSegment=%d, scaledOffset=%d\n", + infoB.instructionAddress, isLDR, literalableSize, withinOneMeg(infoB.instructionAddress, infoA.targetAddress), usableSegment, ldrInfoB.offset); + } + break; + case LOH_ARM64_ADRP_ADD_LDR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isADD = parseADD(infoB.instruction, addInfoB); + LOH_ASSERT(isADD); + LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); + isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC); + LOH_ASSERT(isLDR); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + targetFourByteAligned = ( ((infoB.targetAddress+ldrInfoC.offset) & 0x3) == 0 ); + literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); + if ( literalableSize && usableSegment && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) { + // can do T1 transformation to LDR literal + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress+ldrInfoC.offset, infoC.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-add-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress); + } + } + else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) { + // can to T4 transformation and turn ADRP/ADD into ADR + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoB.instructionAddress); + } + else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) { + // can do T2 transformation by merging ADD into LD + // Leave ADRP as-is + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset += addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-ldr at 0x%08llX T2 transformed to ADRP/LDR \n", infoC.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-ldr at 0x%08llX could not be transformed, loadSize=%d, literalableSize=%d, inRange=%d, usableSegment=%d, targetFourByteAligned=%d, imm12=%d\n", + infoC.instructionAddress, ldrInfoC.size, literalableSize, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, targetFourByteAligned, ldrInfoC.offset); + } + break; + case LOH_ARM64_ADRP_ADD: + LOH_ASSERT(alt.info.count == 1); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isADD = parseADD(infoB.instruction, addInfoB); + LOH_ASSERT(isADD); + LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + if ( usableSegment && withinOneMeg(infoA.targetAddress, infoA.instructionAddress) ) { + // can do T4 transformation and use ADR + set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add at 0x%08llX transformed to ADR\n", infoB.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add at 0x%08llX not transformed, isAdd=%d, inRange=%d, usableSegment=%d\n", + infoB.instructionAddress, isADD, withinOneMeg(infoA.targetAddress, infoA.instructionAddress), usableSegment); + } + break; + case LOH_ARM64_ADRP_LDR_GOT_LDR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup, true)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup, true)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isLDR = parseLoadOrStore(infoC.instruction, ldrInfoC); + LOH_ASSERT(isLDR); + LOH_ASSERT(ldrInfoC.offset == 0); + isADD = parseADD(infoB.instruction, addInfoB); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + if ( isLDR ) { + // target of GOT is external + LOH_ASSERT(ldrInfoB.size == 8); + LOH_ASSERT(!ldrInfoB.isFloat); + LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); + //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + // can do T5 transform + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T5 transformed to LDR literal of GOT plus LDR\n", infoC.instructionAddress); + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX no optimization done\n", infoC.instructionAddress); + } + } + else if ( isADD ) { + // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target + LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); + if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) { + // can do T1 transform + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLDR_literal(ldrInfoC, infoA.targetAddress, infoC.instructionAddress)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T1 transformed to LDR literal\n", infoC.instructionAddress); + } + else if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { + // can do T4 transform + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADR/LDR\n", infoC.instructionAddress); + } + } + else if ( (infoA.targetAddress % ldrInfoC.size) == 0 ) { + // can do T2 transform + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.baseReg = adrpInfoA.destReg; + ldrInfoC.offset = addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T4 transformed to ADRP/NOP/LDR\n", infoC.instructionAddress); + } + } + else { + // T3 transform already done by ld::passes:got:doPass() + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX T3 transformed to ADRP/ADD/LDR\n", infoC.instructionAddress); + } + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-ldr at 0x%08llX not ADD or LDR\n", infoC.instructionAddress); + } + break; + case LOH_ARM64_ADRP_ADD_STR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isADD = parseADD(infoB.instruction, addInfoB); + LOH_ASSERT(isADD); + LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); + isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore); + LOH_ASSERT(isSTR); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress+ldrInfoC.offset) ) { + // can to T4 transformation and turn ADRP/ADD into ADR + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress+ldrInfoC.offset, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset = 0; // offset is now in ADR instead of ADD or LDR + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + set32LE(infoC.instructionContent, infoC.instruction & 0xFFC003FF); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-str at 0x%08llX T4 transformed to ADR/STR\n", infoB.instructionAddress); + } + else if ( ((infoB.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) { + // can do T2 transformation by merging ADD into STR + // Leave ADRP as-is + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.offset += addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-str at 0x%08llX T2 transformed to ADRP/STR \n", infoC.instructionAddress); + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-add-str at 0x%08llX could not be transformed, loadSize=%d, inRange=%d, usableSegment=%d, imm12=%d\n", + infoC.instructionAddress, ldrInfoC.size, withinOneMeg(infoC.instructionAddress, infoA.targetAddress+ldrInfoC.offset), usableSegment, ldrInfoC.offset); + } + break; + case LOH_ARM64_ADRP_LDR_GOT_STR: + LOH_ASSERT(alt.info.count == 2); + LOH_ASSERT(isPageKind(infoA.fixup, true)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup, true)); + LOH_ASSERT(infoC.fixup == NULL); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + LOH_ASSERT(isADRP); + isSTR = (parseLoadOrStore(infoC.instruction, ldrInfoC) && ldrInfoC.isStore); + LOH_ASSERT(isSTR); + LOH_ASSERT(ldrInfoC.offset == 0); + isADD = parseADD(infoB.instruction, addInfoB); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + if ( isLDR ) { + // target of GOT is external + LOH_ASSERT(ldrInfoB.size == 8); + LOH_ASSERT(!ldrInfoB.isFloat); + LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + // can do T5 transform + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T5 transformed to LDR literal of GOT plus STR\n", infoC.instructionAddress); + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX no optimization done\n", infoC.instructionAddress); + } + } + else if ( isADD ) { + // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target + LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); + LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); + literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); + if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { + // can do T4 transform + set32LE(infoA.instructionContent, makeADR(ldrInfoC.baseReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress); + } + } + else if ( ((infoA.targetAddress % ldrInfoC.size) == 0) && (ldrInfoC.offset == 0) ) { + // can do T2 transform + set32LE(infoB.instructionContent, makeNOP()); + ldrInfoC.baseReg = adrpInfoA.destReg; + ldrInfoC.offset = addInfoB.addend; + set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T4 transformed to ADRP/NOP/STR\n", infoC.instructionAddress); + } + } + else { + // T3 transform already done by ld::passes:got:doPass() + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX T3 transformed to ADRP/ADD/STR\n", infoC.instructionAddress); + } + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got-str at 0x%08llX not ADD or LDR\n", infoC.instructionAddress); + } + break; + case LOH_ARM64_ADRP_LDR_GOT: + LOH_ASSERT(alt.info.count == 1); + LOH_ASSERT(isPageKind(infoA.fixup, true)); + LOH_ASSERT(isPageOffsetKind(infoB.fixup, true)); + LOH_ASSERT(infoA.target == infoB.target); + LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); + isADRP = parseADRP(infoA.instruction, adrpInfoA); + isADD = parseADD(infoB.instruction, addInfoB); + isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); + if ( isADRP ) { + if ( isLDR ) { + if ( usableSegment && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { + // can do T5 transform (LDR literal load of GOT) + set32LE(infoA.instructionContent, makeNOP()); + set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got at 0x%08llX T5 transformed to NOP/LDR\n", infoC.instructionAddress); + } + } + } + else if ( isADD ) { + if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { + // can do T4 transform (ADR to compute local address) + set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress)); + set32LE(infoB.instructionContent, makeNOP()); + if ( _options.verboseOptimizationHints() ) { + fprintf(stderr, "adrp-ldr-got at 0x%08llX T4 transformed to ADR/STR\n", infoC.instructionAddress); + } + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got at 0x%08llX not LDR or ADD\n", infoB.instructionAddress); + } + } + else { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "adrp-ldr-got at 0x%08llX not ADRP\n", infoA.instructionAddress); + } + break; + default: + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "unknown hint kind %d alt.info.kind at 0x%08llX\n", alt.info.kind, infoA.instructionAddress); + break; + } + } + // apply hints pass 2 + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->kind != ld::Fixup::kindLinkerOptimizationHint ) + continue; + InstructionInfo infoA; + InstructionInfo infoB; + ld::Fixup::LOH_arm64 alt; + alt.addend = fit->u.addend; + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta1 << 2), &infoA); + if ( alt.info.count > 0 ) + setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta2 << 2), &infoB); + + switch ( alt.info.kind ) { + case LOH_ARM64_ADRP_ADRP: + LOH_ASSERT(isPageKind(infoA.fixup)); + LOH_ASSERT(isPageKind(infoB.fixup)); + if ( (infoA.instruction & 0x9F000000) != 0x90000000 ) { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoA.instructionAddress, infoA.instruction); + sAdrpNA++; + break; + } + if ( (infoB.instruction & 0x9F000000) != 0x90000000 ) { + if ( _options.verboseOptimizationHints() ) + fprintf(stderr, "may-reused-adrp at 0x%08llX no longer an ADRP, now 0x%08X\n", infoB.instructionAddress, infoA.instruction); + sAdrpNA++; + break; + } + if ( (infoA.targetAddress & (-4096)) == (infoB.targetAddress & (-4096)) ) { + set32LE(infoB.instructionContent, 0xD503201F); + sAdrpNoped++; + } + else { + sAdrpNotNoped++; + } + break; + } } } + + + + } void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb) @@ -1608,6 +2555,12 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) } } } + + if ( _options.verboseOptimizationHints() ) { + //fprintf(stderr, "ADRP optimized away: %d\n", sAdrpNA); + //fprintf(stderr, "ADRPs changed to NOPs: %d\n", sAdrpNoped); + //fprintf(stderr, "ADRPs unchanged: %d\n", sAdrpNotNoped); + } } @@ -1746,8 +2699,15 @@ void OutputFile::writeOutputFile(ld::Internal& state) fd = open(tmpOutput, O_RDWR|O_CREAT, permissions); } if ( fd == -1 ) - throwf("can't open output file for writing: %s, errno=%d", tmpOutput, errno); - ftruncate(fd, _fileSize); + throwf("can't open output file for writing '%s', errno=%d", tmpOutput, errno); + if ( ftruncate(fd, _fileSize) == -1 ) { + int err = errno; + unlink(tmpOutput); + if ( err == ENOSPC ) + throwf("not enough disk space for writing '%s'", _options.outputFilePath()); + else + throwf("can't grow file for writing '%s', errno=%d", _options.outputFilePath(), err); + } wholeBuffer = (uint8_t *)mmap(NULL, _fileSize, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); if ( wholeBuffer == MAP_FAILED ) @@ -1792,6 +2752,10 @@ void OutputFile::writeOutputFile(ld::Internal& state) if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) { throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); } + ::close(fd); + // NFS: iOS incremental builds in Xcode 4.6 fail with codesign error + // NFS seems to pad the end of the file sometimes. Calling trunc seems to correct it... + ::truncate(_options.outputFilePath(), _fileSize); } } @@ -1837,7 +2801,7 @@ void OutputFile::buildSymbolTable(ld::Internal& state) // in -r mode, clarify symbolTableNotInFinalLinkedImages if ( _options.outputKind() == Options::kObjectFile ) { - if ( _options.architecture() == CPU_TYPE_X86_64 ) { + if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) { // x86_64 .o files need labels on anonymous literal strings if ( (sect->type() == ld::Section::typeCString) && (atom->combine() == ld::Atom::combineByNameAndContent) ) { (const_cast(atom))->setSymbolTableInclusion(ld::Atom::symbolTableIn); @@ -1929,7 +2893,6 @@ void OutputFile::buildSymbolTable(ld::Internal& state) case ld::Atom::scopeLinkageUnit: if ( _options.outputKind() == Options::kObjectFile ) { if ( _options.keepPrivateExterns() ) { - assert( (atom->combine() == ld::Atom::combineNever) || (atom->combine() == ld::Atom::combineByName) ); _exportedAtoms.push_back(atom); } else if ( _options.keepLocalSymbol(atom->name()) ) { @@ -2049,8 +3012,28 @@ void OutputFile::addPreloadLinkEdit(ld::Internal& state) } break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; +#endif default: - throw "architecture not supported for -preload"; + throw "-preload not supported"; } } @@ -2101,6 +3084,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2159,6 +3146,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2217,6 +3208,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); dataInCodeSection = state.addAtom(*_dataInCodeAtom); } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } if ( _hasDependentDRInfo ) { _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); @@ -2237,6 +3232,68 @@ void OutputFile::addLinkEdit(ld::Internal& state) } break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( _hasSectionRelocations ) { + _sectionsRelocationsAtom = new SectionRelocationsAtom(_options, state, *this); + sectionRelocationsSection = state.addAtom(*_sectionsRelocationsAtom); + } + if ( _hasDyldInfo ) { + _rebasingInfoAtom = new RebaseInfoAtom(_options, state, *this); + rebaseSection = state.addAtom(*_rebasingInfoAtom); + + _bindingInfoAtom = new BindingInfoAtom(_options, state, *this); + bindingSection = state.addAtom(*_bindingInfoAtom); + + _weakBindingInfoAtom = new WeakBindingInfoAtom(_options, state, *this); + weakBindingSection = state.addAtom(*_weakBindingInfoAtom); + + _lazyBindingInfoAtom = new LazyBindingInfoAtom(_options, state, *this); + lazyBindingSection = state.addAtom(*_lazyBindingInfoAtom); + + _exportInfoAtom = new ExportInfoAtom(_options, state, *this); + exportSection = state.addAtom(*_exportInfoAtom); + } + if ( _hasLocalRelocations ) { + _localRelocsAtom = new LocalRelocationsAtom(_options, state, *this); + localRelocationsSection = state.addAtom(*_localRelocsAtom); + } + if ( _hasSplitSegInfo ) { + _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); + } + if ( _hasFunctionStartsInfo ) { + _functionStartsAtom = new FunctionStartsAtom(_options, state, *this); + functionStartsSection = state.addAtom(*_functionStartsAtom); + } + if ( _hasDataInCodeInfo ) { + _dataInCodeAtom = new DataInCodeAtom(_options, state, *this); + dataInCodeSection = state.addAtom(*_dataInCodeAtom); + } + if ( _hasOptimizationHints ) { + _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); + optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); + } + if ( _hasDependentDRInfo ) { + _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); + dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); + } + if ( _hasSymbolTable ) { + _symbolTableAtom = new SymbolTableAtom(_options, state, *this); + symbolTableSection = state.addAtom(*_symbolTableAtom); + } + if ( _hasExternalRelocations ) { + _externalRelocsAtom = new ExternalRelocationsAtom(_options, state, *this); + externalRelocationsSection = state.addAtom(*_externalRelocsAtom); + } + if ( _hasSymbolTable ) { + _indirectSymbolTableAtom = new IndirectSymbolTableAtom(_options, state, *this); + indirectSymbolTableSection = state.addAtom(*_indirectSymbolTableAtom); + _stringPoolAtom = new StringPoolAtom(_options, state, *this, 4); + stringPoolSection = state.addAtom(*_stringPoolAtom); + } + break; +#endif default: throw "unknown architecture"; } @@ -2257,6 +3314,12 @@ void OutputFile::addLoadCommands(ld::Internal& state) headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); + headerAndLoadCommandsSection = state.addAtom(*_headersAndLoadCommandAtom); + break; +#endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: _headersAndLoadCommandAtom = new HeaderAndLoadCommandsAtom(_options, state, *this); @@ -2430,8 +3493,26 @@ bool OutputFile::isPcRelStore(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64PageOff12: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreARM64PCRelToGOT: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: +#endif return true; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif return (_options.outputKind() != Options::kKextBundle); default: break; @@ -2476,15 +3557,27 @@ bool OutputFile::setsTarget(ld::Fixup::Kind kind) case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMLoad12: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: +#endif return true; case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: return (_options.outputKind() == Options::kObjectFile); @@ -2562,13 +3655,14 @@ uint64_t OutputFile::lookBackAddend(ld::Fixup::iterator fit) } - - - void OutputFile::generateLinkEditInfo(ld::Internal& state) { for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; + // record end of last __TEXT section encrypted iPhoneOS apps. + if ( _options.makeEncryptable() && (strcmp(sect->segmentName(), "__TEXT") == 0) ) { + _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); + } bool objc1ClassRefSection = ( (sect->type() == ld::Section::typeCStringPointer) && (strcmp(sect->sectionName(), "__cls_refs") == 0) && (strcmp(sect->segmentName(), "__OBJC") == 0) ); @@ -2590,6 +3684,7 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) ld::Fixup* fixupWithTarget = NULL; ld::Fixup* fixupWithMinusTarget = NULL; ld::Fixup* fixupWithStore = NULL; + ld::Fixup* fixupWithAddend = NULL; const ld::Atom* target = NULL; const ld::Atom* minusTarget = NULL; uint64_t targetAddend = 0; @@ -2624,9 +3719,11 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) switch ( fit->kind ) { case ld::Fixup::kindAddAddend: targetAddend = fit->u.addend; + fixupWithAddend = fit; break; case ld::Fixup::kindSubtractAddend: minusTargetAddend = fit->u.addend; + fixupWithAddend = fit; break; case ld::Fixup::kindSubtractTargetAddress: switch ( fit->binding ) { @@ -2662,7 +3759,7 @@ void OutputFile::generateLinkEditInfo(ld::Internal& state) if ( fit->lastInCluster() ) { if ( (fixupWithStore != NULL) && (target != NULL) ) { if ( _options.outputKind() == Options::kObjectFile ) { - this->addSectionRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithStore, + this->addSectionRelocs(state, sect, atom, fixupWithTarget, fixupWithMinusTarget, fixupWithAddend, fixupWithStore, target, minusTarget, targetAddend, minusTargetAddend); } else { @@ -2701,10 +3798,19 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) else if ( _options.positionIndependentExecutable() && (_options.outputKind() == Options::kDynamicExecutable) && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) { if ( ! this->pieDisabled ) { - warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " +#if SUPPORT_ARCH_arm64 + if ( _options.architecture() == CPU_TYPE_ARM64 ) { + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); + throwf("Absolute addressing not allowed in arm64 code but used in '%s' referencing '%s'", demangledName, _options.demangleSymbol(target->name())); + } + else +#endif + { + warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " "but used in %s from %s. " "To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie", atom->name(), atom->file()->path()); + } } this->pieDisabled = true; } @@ -2712,7 +3818,10 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); } else { - throwf("illegal text-relocation to %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + if ( (target->file() != NULL) && (atom->file() != NULL) ) + throwf("illegal text-relocation to '%s' in %s from '%s' in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + else + throwf("illegal text reloc in '%s' to '%s'", atom->name(), target->name()); } } @@ -2736,6 +3845,11 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols return; } + // spurious warning when weak function has reference to itself + if ( fixupWithTarget->binding == ld::Fixup::bindingDirectlyBound ) { + // ok to ignore pc-rel references within a weak function to itself + return; + } // Have direct reference to weak-global. This should be an indrect reference const char* demangledName = strdup(_options.demangleSymbol(atom->name())); warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. " @@ -2782,7 +3896,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s if ( target == NULL ) return; - bool inReadOnlySeg = ( strcmp(sect->segmentName(), "__TEXT") == 0 ); + bool inReadOnlySeg = ((_options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE) == 0); bool needsRebase = false; bool needsBinding = false; bool needsLazyBinding = false; @@ -2817,8 +3931,11 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s // this will be done by other cluster on lazy pointer atom } } - else if ( (target->contentType() == ld::Atom::typeResolver) && (target->scope() != ld::Atom::scopeGlobal) ) { + else if ( target->contentType() == ld::Atom::typeResolver ) { // Hidden resolver functions should not have lazy binding info + // Resolver function run before initializers when overriding the dyld shared cache + // The lazy pointers used by stubs used when non-lazy binding to a resolver are not normal lazy pointers + // and should not be in lazy binding info. needsLazyBinding = false; } else { @@ -2873,6 +3990,9 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s needsRebase = false; needsBinding = true; } + else if ( _options.forceCoalesce(target->name()) ) { + needsWeakBinding = true; + } } break; case ld::Atom::definitionAbsolute: @@ -2880,6 +4000,20 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s } } + // if target is an import alias, use base of alias + if ( target->isAlias() && (target->definition() == ld::Atom::definitionProxy) ) { + for (ld::Fixup::iterator fit = target->fixupsBegin(), end=target->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { + //fprintf(stderr, "switching import of %s to import of %s\n", target->name(), fit->u.target->name()); + target = fit->u.target; + } + } + } + } + } + // record dyld info for this cluster if ( needsRebase ) { if ( inReadOnlySeg ) { @@ -2887,18 +4021,24 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s sect->hasLocalRelocs = true; // so dyld knows to change permissions on __TEXT segment rebaseType = REBASE_TYPE_TEXT_ABSOLUTE32; } - if ( (addend != 0) && _options.sharedRegionEligible() ) { - // make sure the addend does not cause the pointer to point outside the target's segment - // if it does, update_dyld_shared_cache will not be able to put this dylib into the shared cache - uint64_t targetAddress = target->finalAddress(); - for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sct = *sit; - uint64_t sctEnd = (sct->address+sct->size); - if ( (sct->address <= targetAddress) && (targetAddress < sctEnd) ) { - if ( (targetAddress+addend) > sctEnd ) { - warning("data symbol %s from %s has pointer to %s + 0x%08llX. " - "That large of an addend may disable %s from being put in the dyld shared cache.", - atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() ); + if ( _options.sharedRegionEligible() ) { + // when range checking, ignore high byte of arm64 addends + uint64_t checkAddend = addend; + if ( _options.architecture() == CPU_TYPE_ARM64 ) + checkAddend &= 0x0FFFFFFFFFFFFFFFULL; + if ( checkAddend != 0 ) { + // make sure the addend does not cause the pointer to point outside the target's segment + // if it does, update_dyld_shared_cache will not be able to put this dylib into the shared cache + uint64_t targetAddress = target->finalAddress(); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sct = *sit; + uint64_t sctEnd = (sct->address+sct->size); + if ( (sct->address <= targetAddress) && (targetAddress < sctEnd) ) { + if ( (targetAddress+checkAddend) > sctEnd ) { + warning("data symbol %s from %s has pointer to %s + 0x%08llX. " + "That large of an addend may disable %s from being put in the dyld shared cache.", + atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() ); + } } } } @@ -3013,7 +4153,8 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio if ( (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) && (_options.outputKind() != Options::kStaticExecutable) - && (_options.outputKind() != Options::kPreload) ) { + && (_options.outputKind() != Options::kPreload) + && (atom != target) ) { needsExternReloc = true; } else if ( _options.outputKind() == Options::kDynamicExecutable ) { @@ -3057,6 +4198,9 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio } break; case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: +#endif if ( _options.outputKind() == Options::kKextBundle ) { assert(target != NULL); if ( target->definition() == ld::Atom::definitionProxy ) { @@ -3088,8 +4232,8 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget) { - if ( _options.architecture() == CPU_TYPE_X86_64 ) { - // x86_64 uses external relocations for everthing that has a symbol + if ( (_options.architecture() == CPU_TYPE_X86_64) || (_options.architecture() == CPU_TYPE_ARM64) ) { + // x86_64 and ARM64 use external relocations for everthing that has a symbol return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn ); } @@ -3127,11 +4271,29 @@ bool OutputFile::useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* t return false; } +bool OutputFile::useSectionRelocAddend(ld::Fixup* fixupWithTarget) +{ +#if SUPPORT_ARCH_arm64 + if ( _options.architecture() == CPU_TYPE_ARM64 ) { + switch ( fixupWithTarget->kind ) { + case ld::Fixup::kindStoreARM64Branch26: + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64PageOff12: + return true; + default: + return false; + } + } +#endif + return false; +} + void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, - ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + ld::Fixup* fixupWithTarget, ld::Fixup* fixupWithMinusTarget, + ld::Fixup* fixupWithAddend, ld::Fixup* fixupWithStore, const ld::Atom* target, const ld::Atom* minusTarget, uint64_t targetAddend, uint64_t minusTargetAddend) { @@ -3155,11 +4317,13 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio bool targetUsesExternalReloc = this->useExternalSectionReloc(atom, target, fixupWithTarget); bool minusTargetUsesExternalReloc = (minusTarget != NULL) && this->useExternalSectionReloc(atom, minusTarget, fixupWithMinusTarget); - // in x86_64 .o files an external reloc means the content contains just the addend - if ( _options.architecture() == CPU_TYPE_X86_64 ) { + // in x86_64 and arm64 .o files an external reloc means the content contains just the addend + if ( (_options.architecture() == CPU_TYPE_X86_64) ||(_options.architecture() == CPU_TYPE_ARM64) ) { if ( targetUsesExternalReloc ) { fixupWithTarget->contentAddendOnly = true; fixupWithStore->contentAddendOnly = true; + if ( this->useSectionRelocAddend(fixupWithStore) && (fixupWithAddend != NULL) ) + fixupWithAddend->contentIgnoresAddend = true; } if ( minusTargetUsesExternalReloc ) fixupWithMinusTarget->contentAddendOnly = true; @@ -3252,11 +4416,25 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) case ld::Fixup::kindStoreX86PCRel32GOTLoad: case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressX86PCRel32: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: case ld::Fixup::kindStoreARMLow16: case ld::Fixup::kindStoreThumbLow16: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64PCRelToGOT: +#endif assert(target != NULL); if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) { _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind)); diff --git a/ld64/src/ld/OutputFile.h b/ld64/src/ld/OutputFile.h index d3567ae..6bb793b 100644 --- a/ld64/src/ld/OutputFile.h +++ b/ld64/src/ld/OutputFile.h @@ -68,11 +68,9 @@ public: uint64_t fileSize() const { return _fileSize; } - bool hasWeakExternalSymbols; bool usesWeakExternalSymbols; bool overridesWeakExternalSymbols; bool _noReExportedDylibs; - bool hasThreadLocalVariableDefinitions; bool pieDisabled; bool hasDataInCode; ld::Internal::FinalSection* headerAndLoadCommandsSection; @@ -84,6 +82,7 @@ public: ld::Internal::FinalSection* splitSegInfoSection; ld::Internal::FinalSection* functionStartsSection; ld::Internal::FinalSection* dataInCodeSection; + ld::Internal::FinalSection* optimizationHintsSection; ld::Internal::FinalSection* dependentDRsSection; ld::Internal::FinalSection* symbolTableSection; ld::Internal::FinalSection* stringPoolSection; @@ -150,11 +149,10 @@ private: void generateLinkEditInfo(ld::Internal& state); void buildSymbolTable(ld::Internal& state); void writeOutputFile(ld::Internal& state); - void assignFileOffsets(ld::Internal& state); - void setSectionSizesAndAlignments(ld::Internal& state); void addSectionRelocs(ld::Internal& state, ld::Internal::FinalSection* sect, const ld::Atom* atom, ld::Fixup* fixupWithTarget, - ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithStore, + ld::Fixup* fixupWithMinusTarget, ld::Fixup* fixupWithAddend, + ld::Fixup* fixupWithStore, const ld::Atom* target, const ld::Atom* minusTarget, uint64_t targetAddend, uint64_t minusTargetAddend); void addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* sect, @@ -169,6 +167,7 @@ private: uint64_t targetAddend, uint64_t minusTargetAddend); bool useExternalSectionReloc(const ld::Atom* atom, const ld::Atom* target, ld::Fixup* fixupWithTarget); + bool useSectionRelocAddend(ld::Fixup* fixupWithTarget); uint64_t pageAlign(uint64_t addr); uint64_t pageAlign(uint64_t addr, uint64_t pageSize); void setLoadCommandsPadding(ld::Internal& state); @@ -199,6 +198,10 @@ private: bool hasZeroForFileOffset(const ld::Section* sect); void printSectionLayout(ld::Internal& state); + + bool checkThumbBranch22Displacement(int64_t displacement); + bool checkArmBranch24Displacement(int64_t displacement); + void rangeCheck8(int64_t delta, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup); void rangeCheck16(int64_t delta, ld::Internal& state, const ld::Atom* atom, @@ -215,6 +218,12 @@ private: const ld::Fixup* fixup); void rangeCheckThumbBranch22(int64_t delta, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup); + void rangeCheckARM64Branch26(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + void rangeCheckARM64Page21(int64_t delta, ld::Internal& state, const ld::Atom* atom, + const ld::Fixup* fixup); + + uint64_t sectionOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); uint64_t tlvTemplateOffsetOf(const ld::Internal& state, const ld::Fixup* fixup); void dumpAtomsBySection(ld::Internal& state, bool); @@ -223,6 +232,19 @@ private: void noteTextReloc(const ld::Atom* atom, const ld::Atom* target); + struct InstructionInfo { + uint32_t offsetInAtom; + const ld::Fixup* fixup; + const ld::Atom* target; + uint64_t targetAddress; + uint8_t* instructionContent; + uint64_t instructionAddress; + uint32_t instruction; + }; + + void setInfo(ld::Internal& state, const ld::Atom* atom, uint8_t* buffer, const std::map& usedHints, + uint32_t offsetInAtom, uint32_t delta, InstructionInfo* info); + static uint16_t get16LE(uint8_t* loc); static void set16LE(uint8_t* loc, uint16_t value); static uint32_t get32LE(uint8_t* loc); @@ -253,6 +275,7 @@ private: bool _hasDynamicSymbolTable; bool _hasLocalRelocations; bool _hasExternalRelocations; + bool _hasOptimizationHints; uint64_t _fileSize; std::map _lazyPointerAddressToInfoOffset; uint32_t _encryptedTEXTstartOffset; @@ -289,6 +312,7 @@ public: class LinkEditAtom* _functionStartsAtom; class LinkEditAtom* _dataInCodeAtom; class LinkEditAtom* _dependentDRInfoAtom; + class LinkEditAtom* _optimizationHintsAtom; }; } // namespace tool diff --git a/ld64/src/ld/Resolver.cpp b/ld64/src/ld/Resolver.cpp index ad4b22d..573cad8 100644 --- a/ld64/src/ld/Resolver.cpp +++ b/ld64/src/ld/Resolver.cpp @@ -132,6 +132,7 @@ public: void setFinalAliasOf() const { (const_cast(this))->setAttributesFromAtom(_aliasOf); + (const_cast(this))->setScope(ld::Atom::scopeGlobal); } private: @@ -281,13 +282,21 @@ void Resolver::initializeState() _internal.objcObjectConstraint = ld::File::objcConstraintGC; _internal.cpuSubType = _options.subArchitecture(); + + // In -r mode, look for -linker_option additions + if ( _options.outputKind() == Options::kObjectFile ) { + ld::relocatable::File::LinkerOptionsList lo = _options.linkerOptions(); + for (relocatable::File::LinkerOptionsList::const_iterator it=lo.begin(); it != lo.end(); ++it) { + doLinkerOption(*it, "command line"); + } + } } void Resolver::buildAtomList() { // each input files contributes initial atoms _atoms.reserve(1024); - _inputFiles.forEachInitialAtom(*this); + _inputFiles.forEachInitialAtom(*this, _internal); _completedInitialObjectFiles = true; @@ -295,12 +304,46 @@ void Resolver::buildAtomList() } +void Resolver::doLinkerOption(const std::vector& linkerOption, const char* fileName) +{ + if ( linkerOption.size() == 1 ) { + const char* lo1 = linkerOption.front(); + if ( strncmp(lo1, "-l", 2) == 0 ) { + _internal.linkerOptionLibraries.insert(&lo1[2]); + } + else { + warning("unknown linker option from object file ignored: '%s' in %s", lo1, fileName); + } + } + else if ( linkerOption.size() == 2 ) { + const char* lo2a = linkerOption[0]; + const char* lo2b = linkerOption[1]; + if ( strcmp(lo2a, "-framework") == 0 ) { + _internal.linkerOptionFrameworks.insert(lo2b); + } + else { + warning("unknown linker option from object file ignored: '%s' '%s' from %s", lo2a, lo2b, fileName); + } + } + else { + warning("unknown linker option from object file ignored, starting with: '%s' from %s", linkerOption.front(), fileName); + } +} + void Resolver::doFile(const ld::File& file) { const ld::relocatable::File* objFile = dynamic_cast(&file); const ld::dylib::File* dylibFile = dynamic_cast(&file); if ( objFile != NULL ) { + // if file has linker options, process them + ld::relocatable::File::LinkerOptionsList* lo = objFile->linkerOptions(); + if ( lo != NULL ) { + for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) { + this->doLinkerOption(*it, file.path()); + } + } + // update which form of ObjC is being used switch ( file.objCConstraint() ) { case ld::File::objcConstraintNone: @@ -312,16 +355,31 @@ void Resolver::doFile(const ld::File& file) throwf("command line specified -objc_gc_only, but file is retain/release based: %s", file.path()); if ( _options.objcGc() ) throwf("command line specified -objc_gc, but file is retain/release based: %s", file.path()); - _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; + if ( !_options.targetIOSSimulator() && (_internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator) ) + _internal.objcObjectConstraint = ld::File::objcConstraintRetainRelease; break; case ld::File::objcConstraintRetainReleaseOrGC: if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseOrGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path()); break; case ld::File::objcConstraintGC: if ( _internal.objcObjectConstraint == ld::File::objcConstraintRetainRelease ) throwf("%s built with incompatible Garbage Collection settings to link with previous .o files", file.path()); _internal.objcObjectConstraint = ld::File::objcConstraintGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but object file (%s) was compiled for MacOSX", file.path()); + break; + case ld::File::objcConstraintRetainReleaseForSimulator: + if ( _internal.objcObjectConstraint == ld::File::objcConstraintNone ) { + if ( !_options.targetIOSSimulator() && (_options.outputKind() != Options::kObjectFile) ) + warning("ObjC object file (%s) was compiled for iOS Simulator, but linking for MacOSX", file.path()); + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + } + else if ( _internal.objcObjectConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) { + _internal.objcObjectConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + } break; } @@ -360,7 +418,16 @@ void Resolver::doFile(const ld::File& file) break; case CPU_TYPE_X86_64: - _internal.cpuSubType = CPU_SUBTYPE_X86_64_ALL; + if ( _options.subArchitecture() != nextObjectSubType ) { + if ( _options.allowSubArchitectureMismatches() ) { + warning("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + else { + throwf("object file %s was built for different x86_64 sub-type (%d) than link command line (%d)", + file.path(), nextObjectSubType, _options.subArchitecture()); + } + } break; } } @@ -376,16 +443,30 @@ void Resolver::doFile(const ld::File& file) throwf("command line specified -objc_gc_only, but dylib is retain/release based: %s", file.path()); if ( _options.objcGc() ) throwf("command line specified -objc_gc, but dylib is retain/release based: %s", file.path()); + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); _internal.objcDylibConstraint = ld::File::objcConstraintRetainRelease; break; case ld::File::objcConstraintRetainReleaseOrGC: if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseOrGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); break; case ld::File::objcConstraintGC: if ( _internal.objcDylibConstraint == ld::File::objcConstraintRetainRelease ) throwf("%s built with incompatible Garbage Collection settings to link with previous dylibs", file.path()); - _internal.objcDylibConstraint = ld::File::objcConstraintGC; + if ( _options.targetIOSSimulator() ) + warning("linking ObjC for iOS Simulator, but dylib (%s) was compiled for MacOSX", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintGC; + break; + case ld::File::objcConstraintRetainReleaseForSimulator: + if ( _internal.objcDylibConstraint == ld::File::objcConstraintNone ) + _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + else if ( _internal.objcDylibConstraint != ld::File::objcConstraintRetainReleaseForSimulator ) { + warning("ObjC dylib (%s) was compiled for iOS Simulator, but dylibs others were compiled for MacOSX", file.path()); + _internal.objcDylibConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + } break; } } @@ -394,7 +475,9 @@ void Resolver::doFile(const ld::File& file) void Resolver::doAtom(const ld::Atom& atom) { - //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s\n", &atom, atom.name(), atom.section().sectionName()); + //fprintf(stderr, "Resolver::doAtom(%p), name=%s, sect=%s, scope=%d\n", &atom, atom.name(), atom.section().sectionName(), atom.scope()); + if ( _ltoCodeGenFinished && (atom.contentType() == ld::Atom::typeLTOtemporary) && (atom.scope() != ld::Atom::scopeTranslationUnit) ) + warning("'%s' is implemented in bitcode, but it was loaded too late", atom.name()); // add to list of known atoms _atoms.push_back(&atom); @@ -510,6 +593,8 @@ bool Resolver::isDtraceProbe(ld::Fixup::Kind kind) case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: case ld::Fixup::kindDtraceExtra: @@ -527,6 +612,8 @@ void Resolver::convertReferencesToIndirect(const ld::Atom& atom) const ld::Atom* dummy; ld::Fixup::iterator end = atom.fixupsEnd(); for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != end; ++fit) { + if ( fit->kind == ld::Fixup::kindLinkerOptimizationHint ) + _internal.someObjectHasOptimizationHints = true; switch ( fit->binding ) { case ld::Fixup::bindingByNameUnbound: if ( isDtraceProbe(fit->kind) && (_options.outputKind() != Options::kObjectFile ) ) { @@ -638,14 +725,34 @@ void Resolver::resolveUndefines() } } } - + + // Use linker options to resolve an remaining undefined symbols + if ( !_internal.linkerOptionLibraries.empty() || !_internal.linkerOptionFrameworks.empty() ) { + std::vector undefineNames; + _symbolTable.undefines(undefineNames); + if ( undefineNames.size() != 0 ) { + for (std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { + const char* undef = *it; + if ( ! _symbolTable.hasName(undef) ) { + _inputFiles.searchLibraries(undef, true, true, false, *this); + } + } + } + } + // create proxies as needed for undefined symbols if ( (_options.undefinedTreatment() != Options::kUndefinedError) || (_options.outputKind() == Options::kObjectFile) ) { std::vector undefineNames; _symbolTable.undefines(undefineNames); for(std::vector::iterator it = undefineNames.begin(); it != undefineNames.end(); ++it) { - // make proxy - this->doAtom(*new UndefinedProxyAtom(*it)); + const char* undefName = *it; + // "ld -r -exported_symbol _foo" has wrong error message if _foo is undefined + bool makeProxy = true; + if ( (_options.outputKind() == Options::kObjectFile) && _options.hasExportMaskList() && _options.shouldExport(undefName) ) + makeProxy = false; + + if ( makeProxy ) + this->doAtom(*new UndefinedProxyAtom(undefName)); } } @@ -697,6 +804,7 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindNoneGroupSubordinate: case ld::Fixup::kindNoneGroupSubordinateFDE: case ld::Fixup::kindNoneGroupSubordinateLSDA: + case ld::Fixup::kindNoneGroupSubordinatePersonality: case ld::Fixup::kindSetTargetAddress: case ld::Fixup::kindSubtractTargetAddress: case ld::Fixup::kindStoreTargetAddressLittleEndian32: @@ -713,6 +821,12 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: +#endif if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() // but a archive loaded .o file may have a forward reference @@ -857,6 +971,7 @@ void Resolver::deadStripOptimize(bool force) if ( _haveLLVMObjs && !force ) { // don't remove combinable atoms, they may come back in lto output _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end()); + _symbolTable.removeDeadAtoms(); } else { _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end()); @@ -1325,6 +1440,9 @@ void Resolver::linkTimeOptimize() if ( ! _haveLLVMObjs ) return; + // LTO: Symbol multiply defined error should specify exactly where the symbol is found + _symbolTable.checkDuplicateSymbols(); + // run LLVM lto code-gen lto::OptimizeOptions optOpt; optOpt.outputFilePath = _options.outputFilePath(); @@ -1338,19 +1456,22 @@ void Resolver::linkTimeOptimize() optOpt.relocatable = (_options.outputKind() == Options::kObjectFile); optOpt.allowTextRelocs = _options.allowTextRelocs(); optOpt.linkerDeadStripping = _options.deadCodeStrip(); + optOpt.needsUnwindInfoSection = _options.needsUnwindInfoSection(); + optOpt.keepDwarfUnwind = _options.keepDwarfUnwind(); optOpt.arch = _options.architecture(); + optOpt.mcpu = _options.mcpuLTO(); optOpt.llvmOptions = &_options.llvmOptions(); std::vector newAtoms; std::vector additionalUndefines; if ( ! lto::optimize(_atoms, _internal, optOpt, *this, newAtoms, additionalUndefines) ) return; // if nothing done - + _ltoCodeGenFinished = true; // add all newly created atoms to _atoms and update symbol table for(std::vector::iterator it = newAtoms.begin(); it != newAtoms.end(); ++it) this->doAtom(**it); - + // some atoms might have been optimized way (marked coalesced), remove them this->removeCoalescedAwayAtoms(); @@ -1365,6 +1486,10 @@ void Resolver::linkTimeOptimize() aliasAtom->setFinalAliasOf(); } + // add any auto-link libraries requested by LTO output to dylibs to search + _inputFiles.addLinkerOptionLibraries(_internal); + _inputFiles.createIndirectDylibs(); + // resolve new undefines (e.g calls to _malloc and _memcpy that llvm compiler conjures up) for(std::vector::iterator uit = additionalUndefines.begin(); uit != additionalUndefines.end(); ++uit) { const char *targetName = *uit; @@ -1402,6 +1527,7 @@ void Resolver::linkTimeOptimize() } else { // last chance to check for undefines + this->resolveUndefines(); this->checkUndefines(true); // check new code does not override some dylib @@ -1442,6 +1568,14 @@ void Resolver::tweakWeakness() } } +void Resolver::dumpAtoms() +{ + fprintf(stderr, "Resolver all atoms:\n"); + for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + fprintf(stderr, " %p name=%s, def=%d\n", atom, atom->name(), atom->definition()); + } +} void Resolver::resolve() { diff --git a/ld64/src/ld/Resolver.h b/ld64/src/ld/Resolver.h index 32d1d50..4a3cd73 100644 --- a/ld64/src/ld/Resolver.h +++ b/ld64/src/ld/Resolver.h @@ -62,7 +62,8 @@ public: : _options(opts), _inputFiles(inputs), _internal(state), _symbolTable(opts, state.indirectBindingTable), _haveLLVMObjs(false), - _completedInitialObjectFiles(false) {} + _completedInitialObjectFiles(false), + _ltoCodeGenFinished(false) {} virtual void doAtom(const ld::Atom&); @@ -89,7 +90,7 @@ private: void fillInInternalState(); void fillInHelpersInInternalState(); void removeCoalescedAwayAtoms(); - void fillInEntryPoint(); + void fillInEntryPoint(); void linkTimeOptimize(); void convertReferencesToIndirect(const ld::Atom& atom); const ld::Atom* entryPoint(bool searchArchives); @@ -99,6 +100,8 @@ private: void remainingUndefines(std::vector&); bool printReferencedBy(const char* name, SymbolTable::IndirectBindingSlot slot); void tweakWeakness(); + void doLinkerOption(const std::vector& linkerOption, const char* fileName); + void dumpAtoms(); typedef std::unordered_set StringSet; @@ -126,6 +129,7 @@ private: SymbolTable _symbolTable; bool _haveLLVMObjs; bool _completedInitialObjectFiles; + bool _ltoCodeGenFinished; }; diff --git a/ld64/src/ld/SymbolTable.cpp b/ld64/src/ld/SymbolTable.cpp index 2ea690f..b271259 100644 --- a/ld64/src/ld/SymbolTable.cpp +++ b/ld64/src/ld/SymbolTable.cpp @@ -312,6 +312,7 @@ private: } void pickAtom() { + //fprintf(stderr, "pickAtom(), a=%p, def=%d, b=%p, def=%d\n", &_atomA, _atomA.definition(), &_atomB, _atomB.definition()); // First, discriminate by definition switch (_atomA.definition()) { case ld::Atom::definitionRegular: @@ -320,6 +321,12 @@ private: pickBetweenRegularAtoms(); break; case ld::Atom::definitionTentative: + if ( _atomB.size() > _atomA.size() ) { + const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : ""; + const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : ""; + warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", + _atomA.name(), _atomB.size(), atomBpath, _atomA.size(), atomApath); + } pickAtomA(); break; case ld::Atom::definitionAbsolute: @@ -334,6 +341,12 @@ private: case ld::Atom::definitionTentative: switch (_atomB.definition()) { case ld::Atom::definitionRegular: + if ( _atomA.size() > _atomB.size() ) { + const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : ""; + const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : ""; + warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", + _atomA.name(), _atomA.size(),atomApath, _atomB.size(), atomBpath); + } pickAtomB(); break; case ld::Atom::definitionTentative: @@ -575,6 +588,19 @@ SymbolTable::IndirectBindingSlot SymbolTable::findSlotForName(const char* name) return slot; } +void SymbolTable::removeDeadAtoms() +{ + for (NameToSlot::iterator it=_byNameTable.begin(); it != _byNameTable.end(); ++it) { + IndirectBindingSlot slot = it->second; + const ld::Atom* atom = _indirectBindingTable[slot]; + if ( atom != NULL ) { + if ( !atom->live() && !atom->dontDeadStrip() ) { + //fprintf(stderr, "removing from symbolTable[%u] %s\n", slot, atom->name()); + _indirectBindingTable[slot] = NULL; + } + } + } +} // find existing or create new slot SymbolTable::IndirectBindingSlot SymbolTable::findSlotForContent(const ld::Atom* atom, const ld::Atom** existingAtom) diff --git a/ld64/src/ld/SymbolTable.h b/ld64/src/ld/SymbolTable.h index 5575f31..14c7a9e 100644 --- a/ld64/src/ld/SymbolTable.h +++ b/ld64/src/ld/SymbolTable.h @@ -120,6 +120,7 @@ public: unsigned int updateCount() { return _indirectBindingTable.size(); } void undefines(std::vector& undefines); void tentativeDefs(std::vector& undefines); + void removeDeadAtoms(); bool hasName(const char* name); bool hasExternalTentativeDefinitions() { return _hasExternalTentativeDefinitions; } byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); } diff --git a/ld64/src/ld/dwarf2.h b/ld64/src/ld/dwarf2.h index 411dbbc..ecdf898 100644 --- a/ld64/src/ld/dwarf2.h +++ b/ld64/src/ld/dwarf2.h @@ -62,9 +62,15 @@ enum { DW_FORM_ref4, DW_FORM_ref8, DW_FORM_ref_udata, - DW_FORM_indirect /* 22 */ + DW_FORM_indirect, + /* new in Dwarf 4 */ + DW_FORM_sec_offset, + DW_FORM_exprloc, + DW_FORM_flag_present, + DW_FORM_ref_sig8 }; + enum { DW_LNS_extended_op = 0, DW_LNS_copy, diff --git a/ld64/src/ld/ld.cpp b/ld64/src/ld/ld.cpp index a5db06e..844f614 100644 --- a/ld64/src/ld/ld.cpp +++ b/ld64/src/ld/ld.cpp @@ -100,9 +100,6 @@ struct PerformanceStatistics { }; - - - class InternalState : public ld::Internal { public: @@ -110,6 +107,8 @@ public: virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom); virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&); + uint64_t assignFileOffsets(); + void setSectionSizesAndAlignments(); void sortSections(); void markAtomsOrdered() { _atomsOrderedInSections = true; } virtual ~InternalState() {} @@ -121,7 +120,7 @@ private: FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile); static int sectionComparer(const void* l, const void* r); static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill); - static const ld::Section& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal); + static const ld::Section& objectOutputSection(const ld::Section& sect, const Options&); private: friend class InternalState; static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen); @@ -139,6 +138,9 @@ private: static ld::Section _s_DATA_zerofill; }; + bool hasZeroForFileOffset(const ld::Section* sect); + uint64_t pageAlign(uint64_t addr); + uint64_t pageAlign(uint64_t addr, uint64_t pageSize); struct SectionHash { size_t operator()(const ld::Section*) const; @@ -243,10 +245,19 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& return sect; } -const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal) +const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, const Options& options) { + const std::vector& renames = options.sectionRenames(); + for ( std::vector::const_iterator it=renames.begin(); it != renames.end(); ++it) { + if ( (strcmp(sect.sectionName(), it->fromSection) == 0) && (strcmp(sect.segmentName(), it->fromSegment) == 0) ) { + ld::Section* s = new ld::Section(it->toSegment, it->toSection, sect.type()); + return *s; + } + } + + // in -r mode the only section that ever changes is __tenative -> __common with -d option - if ( (sect.type() == ld::Section::typeTentativeDefs) && makeTentativeDefsReal) + if ( (sect.type() == ld::Section::typeTentativeDefs) && options.makeTentativeDefinitionsReal()) return _s_DATA_common; return sect; } @@ -333,8 +344,11 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint else return INT_MAX-2; default: + // __DATA,__const section should be near __mod_init_func not __data + if ( strcmp(sect.sectionName(), "__const") == 0 ) + return 14; // Reorder sections to reduce page faults in object files - if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) + else if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) return 20; else if ( strcmp(sect.sectionName(), "__objc_nlclslist") == 0 ) return 21; @@ -519,7 +533,7 @@ ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& in } break; case Options::kObjectFile: - baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options.makeTentativeDefinitionsReal()); + baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options); pos = _sectionInToFinalMap.find(baseForFinalSection); if ( pos != _sectionInToFinalMap.end() ) { _sectionInToFinalMap[&inputSection] = pos->second; @@ -566,6 +580,308 @@ void InternalState::sortSections() } + +bool InternalState::hasZeroForFileOffset(const ld::Section* sect) +{ + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTLVZeroFill: + return _options.optimizeZeroFill(); + case ld::Section::typePageZero: + case ld::Section::typeStack: + case ld::Section::typeTentativeDefs: + return true; + default: + break; + } + return false; +} + +uint64_t InternalState::pageAlign(uint64_t addr) +{ + const uint64_t alignment = _options.segmentAlignment(); + return ((addr+alignment-1) & (-alignment)); +} + +uint64_t InternalState::pageAlign(uint64_t addr, uint64_t pageSize) +{ + return ((addr+pageSize-1) & (-pageSize)); +} + +void InternalState::setSectionSizesAndAlignments() +{ + for (std::vector::iterator sit = sections.begin(); sit != sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { + // absolute symbols need their finalAddress() to their value + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + (const_cast(atom))->setSectionOffset(atom->objectAddress()); + } + } + else { + uint16_t maxAlignment = 0; + uint64_t offset = 0; + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + bool pagePerAtom = false; + uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; + uint32_t atomModulus = atom->alignment().modulus; + if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) { + // most objc sections cannot be padded + bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 ); + if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 ) + contiguousObjCSection = false; + if ( strcmp(atom->section().sectionName(), "__objc_data") == 0 ) + contiguousObjCSection = false; + switch ( atom->section().type() ) { + case ld::Section::typeUnclassified: + case ld::Section::typeTentativeDefs: + case ld::Section::typeZeroFill: + if ( contiguousObjCSection ) + break; + pagePerAtom = true; + if ( atomAlignmentPowerOf2 < 12 ) { + atomAlignmentPowerOf2 = 12; + atomModulus = 0; + } + break; + default: + break; + } + } + if ( atomAlignmentPowerOf2 > maxAlignment ) + maxAlignment = atomAlignmentPowerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atomAlignmentPowerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atomModulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + // LINKEDIT atoms are laid out later + if ( sect->type() != ld::Section::typeLinkEdit ) { + (const_cast(atom))->setSectionOffset(offset); + offset += atom->size(); + if ( pagePerAtom ) { + offset = (offset + 4095) & (-4096); // round up to end of page + } + } + if ( (atom->scope() == ld::Atom::scopeGlobal) + && (atom->definition() == ld::Atom::definitionRegular) + && (atom->combine() == ld::Atom::combineByName) + && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) + || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { + this->hasWeakExternalSymbols = true; + if ( _options.warnWeakExports() ) + warning("weak external symbol: %s", atom->name()); + } + } + sect->size = offset; + // section alignment is that of a contained atom with the greatest alignment + sect->alignment = maxAlignment; + // unless -sectalign command line option overrides + if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) + sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); + // each atom in __eh_frame has zero alignment to assure they pack together, + // but compilers usually make the CFIs pointer sized, so we want whole section + // to start on pointer sized boundary. + if ( sect->type() == ld::Section::typeCFI ) + sect->alignment = 3; + if ( sect->type() == ld::Section::typeTLVDefs ) + this->hasThreadLocalVariableDefinitions = true; + } + } +} + +uint64_t InternalState::assignFileOffsets() +{ + const bool log = false; + const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kPreload)); + const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); + + uint64_t address = 0; + const char* lastSegName = ""; + uint64_t floatingAddressStart = _options.baseAddress(); + + // first pass, assign addresses to sections in segments with fixed start addresses + if ( log ) fprintf(stderr, "Fixed address segments:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + address = _options.customSegmentAddress(sect->segmentName()); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + + // if TEXT segment address is fixed, then flow other segments after it + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + floatingAddressStart = address; + } + } + + // second pass, assign section address to sections in segments that are contiguous with previous segment + address = floatingAddressStart; + lastSegName = ""; + ld::Internal::FinalSection* overlappingFixedSection = NULL; + ld::Internal::FinalSection* overlappingFlowSection = NULL; + if ( log ) fprintf(stderr, "Regular layout segments:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) + continue; + if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { + sect->alignmentPaddingBytes = 0; + continue; + } + if ( segmentsArePageAligned ) { + if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { + // round up size of last segment if needed + if ( *lastSegName != '\0' ) { + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // set segment address based on end of last segment + address = pageAlign(address); + lastSegName = sect->segmentName(); + } + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + + // update section info + sect->address = address; + sect->alignmentPaddingBytes = (address - unalignedAddress); + + // sanity check size + if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) + && (_options.outputKind() != Options::kStaticExecutable) ) + throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", + sect->sectionName(), address, sect->size); + + // sanity check it does not overlap a fixed address segment + for (std::vector::iterator sit = sections.begin(); sit != sections.end(); ++sit) { + ld::Internal::FinalSection* otherSect = *sit; + if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) + continue; + if ( sect->address > otherSect->address ) { + if ( (otherSect->address+otherSect->size) > sect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + else { + if ( (sect->address+sect->size) > otherSect->address ) { + overlappingFixedSection = otherSect; + overlappingFlowSection = sect; + } + } + } + + if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", + sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + } + if ( overlappingFixedSection != NULL ) { + fprintf(stderr, "Section layout:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( sect->isSectionHidden() ) + continue; + fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", + sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, + sect->segmentName(), sect->sectionName()); + + } + throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", + overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), + overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); + } + + + // third pass, assign section file offsets + uint64_t fileOffset = 0; + lastSegName = ""; + if ( log ) fprintf(stderr, "All segments with file offsets:\n"); + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( hasZeroForFileOffset(sect) ) { + // fileoff of zerofill sections is moot, but historically it is set to zero + sect->fileOffset = 0; + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + } + else { + // page align file offset at start of each segment + if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { + fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); + } + lastSegName = sect->segmentName(); + + // align file offset with address layout + fileOffset += sect->alignmentPaddingBytes; + + // update section info + sect->fileOffset = fileOffset; + + // update running total + fileOffset += sect->size; + } + + if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", + sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, + sect->segmentName(), sect->sectionName()); + } + +#if 0 + // for encrypted iPhoneOS apps + if ( _options.makeEncryptable() ) { + // remember end of __TEXT for later use by load command + for (std::vector::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { + _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); + } + } + } +#endif + + // return total file size + return fileOffset; +} + static char* commatize(uint64_t in, char* out) { char* result = out; @@ -587,10 +903,9 @@ static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) static uint64_t sUnitsPerSecond = 0; if ( sUnitsPerSecond == 0 ) { struct mach_timebase_info timeBaseInfo; - if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { - sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; - //fprintf(stderr, "sUnitsPerSecond=%llu\n", sUnitsPerSecond); - } + if ( mach_timebase_info(&timeBaseInfo) != KERN_SUCCESS ) + return; + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; } if ( partTime < sUnitsPerSecond ) { uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; diff --git a/ld64/src/ld/ld.hpp b/ld64/src/ld/ld.hpp index 7be00a3..95973aa 100644 --- a/ld64/src/ld/ld.hpp +++ b/ld64/src/ld/ld.hpp @@ -32,8 +32,9 @@ #include #include -#include +#include +#include "configure.h" namespace ld { @@ -51,7 +52,9 @@ namespace ld { class File { public: - enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease, objcConstraintRetainReleaseOrGC, objcConstraintGC }; + enum ObjcConstraint { objcConstraintNone, objcConstraintRetainRelease, + objcConstraintRetainReleaseOrGC, objcConstraintGC, + objcConstraintRetainReleaseForSimulator }; class AtomHandler { public: @@ -84,7 +87,7 @@ public: Ordinal (uint64_t ordinal) : _ordinal(ordinal) {} - enum { ArgListPartition=0, IndirectDylibPartition=1, LTOPartition = 2, InvalidParition=0xffff }; + enum { kArgListPartition=0, kIndirectDylibPartition=1, kLTOPartition = 2, kLinkerOptionPartition = 2, InvalidParition=0xffff }; Ordinal(uint16_t partition, uint16_t majorIndex, uint16_t minorIndex, uint16_t counter) { _ordinal = ((uint64_t)partition<<48) | ((uint64_t)majorIndex<<32) | ((uint64_t)minorIndex<<16) | ((uint64_t)counter<<0); } @@ -115,16 +118,21 @@ public: // The minorIndex is used for files pulled in by a file list and the value is the index of the file in the file list. // The counter is used for .a files and the value is the index of the object in the archive. // Thus, an object pulled in from a .a that was listed in a file list could use all three fields. - static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(ArgListPartition, argIndex, 0, 0); }; + static const Ordinal makeArgOrdinal(uint16_t argIndex) { return Ordinal(kArgListPartition, argIndex, 0, 0); }; const Ordinal nextFileListOrdinal() const { return nextMinorIndex(); } - const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(ArgListPartition, majorIndex(), minorIndex(), index); } + const Ordinal archiveOrdinalWithMemberIndex(uint16_t index) const { return Ordinal(kArgListPartition, majorIndex(), minorIndex(), index); } // For indirect libraries the partition is IndirectDylibPartition and the counter is used or order the libraries. - static const ld::File::Ordinal indirectDylibBase() { return Ordinal(IndirectDylibPartition, 0, 0, 0); } + static const ld::File::Ordinal indirectDylibBase() { return Ordinal(kIndirectDylibPartition, 0, 0, 0); } const Ordinal nextIndirectDylibOrdinal() const { return nextCounter(); } // For the LTO mach-o the partition is LTOPartition. As there is only one LTO file no other fields are needed. - static const ld::File::Ordinal LTOOrdinal() { return Ordinal(LTOPartition, 0, 0, 0); } + static const ld::File::Ordinal LTOOrdinal() { return Ordinal(kLTOPartition, 0, 0, 0); } + + // For linker options embedded in object files + static const ld::File::Ordinal linkeOptionBase() { return Ordinal(kIndirectDylibPartition, 1, 0, 0); } + const Ordinal nextLinkerOptionOrdinal() { nextCounter(); return *this; }; + }; typedef enum { Reloc, Dylib, Archive, Other } Type; @@ -155,10 +163,11 @@ private: // enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500, mac10_6=0x000A0600, mac10_7=0x000A0700, mac10_8=0x000A0800, - mac10_Future=0x10000000 }; + mac10_9=0x000A0900, mac10_Future=0x10000000 }; enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, - iOS_6_0=0x00060000, iOS_Future=0x10000000}; + iOS_6_0=0x00060000, iOS_7_0=0x00070000, + iOS_Future=0x10000000}; namespace relocatable { // @@ -189,6 +198,7 @@ namespace relocatable { uint32_t value; const char* string; }; + typedef const std::vector< std::vector > LinkerOptionsList; File(const char* pth, time_t modTime, Ordinal ord) : ld::File(pth, modTime, ord, Reloc) { } @@ -199,6 +209,7 @@ namespace relocatable { virtual const std::vector* stabs() const = 0; virtual bool canScatterAtoms() const = 0; virtual bool hasLongBranchStubs() { return false; } + virtual LinkerOptionsList* linkerOptions() const = 0; }; } // namespace relocatable @@ -258,6 +269,7 @@ namespace dylib { virtual bool hasPublicInstallName() const = 0; virtual bool allSymbolsAreWeakImported() const = 0; virtual const void* codeSignatureDR() const = 0; + virtual bool installPathVersionSpecific() const { return false; } protected: const char* _dylibInstallPath; uint32_t _dylibTimeStamp; @@ -391,16 +403,31 @@ struct Fixup kindStoreARMLoad12, kindStoreARMLow16, kindStoreARMHigh16, kindStoreThumbLow16, kindStoreThumbHigh16, +#if SUPPORT_ARCH_arm64 + // ARM64 specific store kinds + kindStoreARM64Branch26, + kindStoreARM64Page21, kindStoreARM64PageOff12, + kindStoreARM64GOTLoadPage21, kindStoreARM64GOTLoadPageOff12, + kindStoreARM64GOTLeaPage21, kindStoreARM64GOTLeaPageOff12, + kindStoreARM64TLVPLoadPage21, kindStoreARM64TLVPLoadPageOff12, + kindStoreARM64TLVPLoadNowLeaPage21, kindStoreARM64TLVPLoadNowLeaPageOff12, + kindStoreARM64PointerToGOT, kindStoreARM64PCRelToGOT, +#endif // dtrace probes kindDtraceExtra, kindStoreX86DtraceCallSiteNop, kindStoreX86DtraceIsEnableSiteClear, kindStoreARMDtraceCallSiteNop, kindStoreARMDtraceIsEnableSiteClear, + kindStoreARM64DtraceCallSiteNop, kindStoreARM64DtraceIsEnableSiteClear, kindStoreThumbDtraceCallSiteNop, kindStoreThumbDtraceIsEnableSiteClear, // lazy binding kindLazyTarget, kindSetLazyOffset, + // islands + kindIslandTarget, // data-in-code markers kindDataInCodeStartData, kindDataInCodeStartJT8, kindDataInCodeStartJT16, kindDataInCodeStartJT32, kindDataInCodeStartJTA32, kindDataInCodeEnd, + // linker optimzation hints + kindLinkerOptimizationHint, // pointer store combinations kindStoreTargetAddressLittleEndian32, // kindSetTargetAddress + kindStoreLittleEndian32 kindStoreTargetAddressLittleEndian64, // kindSetTargetAddress + kindStoreLittleEndian64 @@ -421,6 +448,20 @@ struct Fixup kindStoreTargetAddressARMBranch24, // kindSetTargetAddress + kindStoreARMBranch24 kindStoreTargetAddressThumbBranch22, // kindSetTargetAddress + kindStoreThumbBranch22 kindStoreTargetAddressARMLoad12, // kindSetTargetAddress + kindStoreARMLoad12 +#if SUPPORT_ARCH_arm64 + // ARM64 value calculation and store combinations + kindStoreTargetAddressARM64Branch26, // kindSetTargetAddress + kindStoreARM64Branch26 + kindStoreTargetAddressARM64Page21, // kindSetTargetAddress + kindStoreARM64Page21 + kindStoreTargetAddressARM64PageOff12, // kindSetTargetAddress + kindStoreARM64PageOff12 + kindStoreTargetAddressARM64GOTLoadPage21, // kindSetTargetAddress + kindStoreARM64GOTLoadPage21 + kindStoreTargetAddressARM64GOTLoadPageOff12,// kindSetTargetAddress + kindStoreARM64GOTLoadPageOff12 + kindStoreTargetAddressARM64GOTLeaPage21, // kindSetTargetAddress + kindStoreARM64GOTLeaPage21 + kindStoreTargetAddressARM64GOTLeaPageOff12, // kindSetTargetAddress + kindStoreARM64GOTLeaPageOff12 + kindStoreTargetAddressARM64TLVPLoadPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadPage21 + kindStoreTargetAddressARM64TLVPLoadPageOff12,// kindSetTargetAddress + kindStoreARM64TLVPLoadPageOff12 + kindStoreTargetAddressARM64TLVPLoadNowLeaPage21, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPage21 + kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12, // kindSetTargetAddress + kindStoreARM64TLVPLoadNowLeaPageOff12 +#endif }; union { @@ -436,54 +477,72 @@ struct Fixup TargetBinding binding : 3; bool contentAddendOnly : 1; bool contentDetlaToAddendOnly : 1; + bool contentIgnoresAddend : 1; typedef Fixup* iterator; Fixup() : offsetInAtom(0), kind(kindNone), clusterSize(k1of1), weakImport(false), binding(bindingNone), - contentAddendOnly(false), contentDetlaToAddendOnly(false) { u.target = NULL; } + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { u.target = NULL; } Fixup(Kind k, Atom* targetAtom) : offsetInAtom(0), kind(k), clusterSize(k1of1), weakImport(false), binding(Fixup::bindingDirectlyBound), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(targetAtom != NULL); u.target = targetAtom; } Fixup(uint32_t off, Cluster c, Kind k) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(Fixup::bindingNone), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { u.addend = 0; } Fixup(uint32_t off, Cluster c, Kind k, bool weakIm, const char* name) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(weakIm), binding(Fixup::bindingByNameUnbound), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(name != NULL); u.name = name; } Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const char* name) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(name != NULL); u.name = name; } Fixup(uint32_t off, Cluster c, Kind k, const Atom* targetAtom) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(Fixup::bindingDirectlyBound), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(targetAtom != NULL); u.target = targetAtom; } Fixup(uint32_t off, Cluster c, Kind k, TargetBinding b, const Atom* targetAtom) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(b), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { assert(targetAtom != NULL); u.target = targetAtom; } Fixup(uint32_t off, Cluster c, Kind k, uint64_t addend) : offsetInAtom(off), kind(k), clusterSize(c), weakImport(false), binding(Fixup::bindingNone), - contentAddendOnly(false), contentDetlaToAddendOnly(false) + contentAddendOnly(false), contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { u.addend = addend; } +#if SUPPORT_ARCH_arm64 + Fixup(Kind k, uint32_t lohKind, uint32_t off1, uint32_t off2) : + offsetInAtom(off1), kind(k), clusterSize(k1of1), + weakImport(false), binding(Fixup::bindingNone), contentAddendOnly(false), + contentDetlaToAddendOnly(false), contentIgnoresAddend(false) { + assert(k == kindLinkerOptimizationHint); + LOH_arm64 extra; + extra.addend = 0; + extra.info.kind = lohKind; + extra.info.count = 1; + extra.info.delta1 = 0; + extra.info.delta2 = (off2 - off1) >> 2; + u.addend = extra.addend; + } +#endif + + bool firstInCluster() const { switch (clusterSize) { case k1of1: @@ -512,6 +571,20 @@ struct Fixup return false; } +#if SUPPORT_ARCH_arm64 + union LOH_arm64 { + uint64_t addend; + struct { + unsigned kind : 6, + count : 2, // 00 => 1 addr, 11 => 4 addrs + delta1 : 14, // 16-bit delta, low 2 bits assumed zero + delta2 : 14, + delta3 : 14, + delta4 : 14; + } info; + }; +#endif + }; // @@ -611,7 +684,7 @@ public: switch ( _combine ) { case combineByNameAndContent: case combineByNameAndReferences: - assert(_symbolTableInclusion == symbolTableNotIn); + assert(_symbolTableInclusion != symbolTableIn); assert(_scope != scopeGlobal); break; case combineByName: @@ -675,6 +748,7 @@ public: } return false; } + virtual void setFile(const File* f) { } virtual UnwindInfo::iterator beginUnwind() const { return NULL; } virtual UnwindInfo::iterator endUnwind() const { return NULL; } @@ -692,7 +766,6 @@ protected: _combine = a._combine; _dontDeadStrip = a._dontDeadStrip; _thumb = a._thumb; - _alias = a._alias; _autoHide = a._autoHide; _contentType = a._contentType; _symbolTableInclusion = a._symbolTableInclusion; @@ -733,6 +806,23 @@ public: }; + +// utility classes for using std::unordered_map with c-strings +struct CStringHash { + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; +struct CStringEquals +{ + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +typedef std::unordered_set CStringSet; + class Internal { public: @@ -757,6 +847,8 @@ public: bool hasExternalRelocs; }; + virtual uint64_t assignFileOffsets() = 0; + virtual void setSectionSizesAndAlignments() = 0; virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0; virtual ld::Internal::FinalSection* getFinalSection(const ld::Section& inputSection) = 0; virtual ~Internal() {} @@ -767,11 +859,16 @@ public: objcDylibConstraint(ld::File::objcConstraintNone), cpuSubType(0), allObjectFilesScatterable(true), - someObjectFileHasDwarf(false), usingHugeSections(false) { } + someObjectFileHasDwarf(false), usingHugeSections(false), + hasThreadLocalVariableDefinitions(false), + hasWeakExternalSymbols(false), + someObjectHasOptimizationHints(false) { } std::vector sections; std::vector dylibs; std::vector stabs; + CStringSet linkerOptionLibraries; + CStringSet linkerOptionFrameworks; std::vector indirectBindingTable; const ld::dylib::File* bundleLoader; const Atom* entryPoint; @@ -784,25 +881,13 @@ public: bool allObjectFilesScatterable; bool someObjectFileHasDwarf; bool usingHugeSections; + bool hasThreadLocalVariableDefinitions; + bool hasWeakExternalSymbols; + bool someObjectHasOptimizationHints; }; - -// utility classes for using std::unordered_map with c-strings -struct CStringHash { - size_t operator()(const char* __s) const { - size_t __h = 0; - for ( ; *__s; ++__s) - __h = 5 * __h + *__s; - return __h; - }; -}; -struct CStringEquals -{ - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } -}; - diff --git a/ld64/src/ld/parsers/archive_file.cpp b/ld64/src/ld/parsers/archive_file.cpp index 708f1fb..9004530 100644 --- a/ld64/src/ld/parsers/archive_file.cpp +++ b/ld64/src/ld/parsers/archive_file.cpp @@ -109,7 +109,7 @@ private: }; - struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint16_t index;}; + struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint32_t index;}; bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const; typedef std::unordered_map NameToEntryMap; @@ -223,6 +223,7 @@ const class File::Entry* File::Entry::next() const template <> cpu_type_t File::architecture() { return CPU_TYPE_I386; } template <> cpu_type_t File::architecture() { return CPU_TYPE_X86_64; } template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM; } +template <> cpu_type_t File::architecture() { return CPU_TYPE_ARM64; } template @@ -323,13 +324,13 @@ bool File::memberHasObjCCategories(const Entry* member) const template typename File::MemberState& File::makeObjectFileForMember(const Entry* member) const { - uint16_t memberIndex = 0; + uint32_t memberIndex = 0; // in case member was instantiated earlier but not needed yet typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member); if ( pos == _instantiatedEntries.end() ) { // Have to find the index of this member const Entry* start; - uint16_t index; + uint32_t index; if (_instantiatedEntries.size() == 0) { start = (Entry*)&_archiveFileContent[8]; index = 1; @@ -380,7 +381,7 @@ typename File::MemberState& File::makeObjectFileForMember(const Entry* mem // see if member is llvm bitcode file result = lto::parse(member->content(), member->contentSize(), mPath, member->modificationTime(), ordinal, - _objOpts.architecture, _objOpts.subType, _logAllFiles); + _objOpts.architecture, _objOpts.subType, _logAllFiles, _objOpts.verboseOptimizationHints); if ( result != NULL ) { MemberState state = {result, member, false, false, memberIndex}; _instantiatedEntries[member] = state; @@ -599,6 +600,12 @@ ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( archive::Parser::validFile(fileContent, fileLength, opts.objOpts) ) + return archive::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; +#endif } return NULL; } diff --git a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp index b04582d..a37c8a0 100644 --- a/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/ld64/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -95,7 +95,9 @@ 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, bool neverConvertToCU, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn); static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, @@ -152,6 +154,18 @@ 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 +173,9 @@ 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, bool neverConvertToCU, + CFI_Atom_Info* infos, uint32_t& infosCount, void* ref, WarnFunc warn) { typename CFI_Parser::CIE_Info cieInfo; CFI_Atom_Info* entry = infos; @@ -221,7 +237,6 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding); pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F); //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange)); - // test if pc is within the function this FDE covers entry->u.fdeInfo.function.targetAddress = pcStart; entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress; entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding; @@ -243,34 +258,60 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS } p = endOfAug; } - // compute compact unwind encoding - typename CFI_Parser::FDE_Info fdeInfo; - fdeInfo.fdeStart = currentCFI; - fdeInfo.fdeLength = nextCFI - currentCFI; - fdeInfo.fdeInstructions = p; - fdeInfo.pcStart = pcStart; - fdeInfo.pcEnd = pcStart + pcRange; - fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; - typename CFI_Parser::PrologInfo prolog; - R dummy; // for proper selection of architecture specific functions - if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { - char warningBuffer[1024]; - entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); - if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) - entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; - if ( warningBuffer[0] != '\0' ) - warn(ref, fdeInfo.pcStart, warningBuffer); + // See if already is a compact unwind for this address. + bool alreadyHaveCU = false; + for (uint32_t i=0; i < cuCount; ++i) { + if (cuStarts[i] == entry->u.fdeInfo.function.targetAddress) { + alreadyHaveCU = true; + break; + } + } + //fprintf(stderr, "FDE for func at 0x%08X, alreadyHaveCU=%d\n", (uint32_t)entry->u.fdeInfo.function.targetAddress, alreadyHaveCU); + if ( alreadyHaveCU && !forceDwarfConversion ) { + if ( keepDwarfWhichHasCU ) + ++entry; } else { - warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); - entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + if ( neverConvertToCU || ((cuCount != 0) && !forceDwarfConversion) ) { + // Have some compact unwind, so this is a new .o file, therefore anything without + // compact unwind must be something not expressable in compact unwind. + R dummy; + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + else { + // compute compact unwind encoding by parsing dwarf + typename CFI_Parser::FDE_Info fdeInfo; + fdeInfo.fdeStart = currentCFI; + fdeInfo.fdeLength = nextCFI - currentCFI; + fdeInfo.fdeInstructions = p; + fdeInfo.pcStart = pcStart; + fdeInfo.pcEnd = pcStart + pcRange; + fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; + typename CFI_Parser::PrologInfo prolog; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + char warningBuffer[1024]; + entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) + entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; + if ( warningBuffer[0] != '\0' ) + warn(ref, fdeInfo.pcStart, warningBuffer); + } + else { + warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + } + ++entry; } - ++entry; } p = nextCFI; } - if ( entry != end ) - return "wrong entry count for parseCFIs"; + if ( entry != end ) { + //fprintf(stderr, "DwarfInstructions::parseCFIs() infosCount was %d on input, now %ld\n", infosCount, entry - infos); + infosCount = (entry - infos); + } + return NULL; // success } @@ -1037,7 +1078,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo for (int i=0; i < 64; ++i) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { - sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_64_MODE_DWARF; } switch (i) { @@ -1184,15 +1225,19 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); return UNWIND_X86_64_MODE_DWARF; } - pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + #if __EXCEPTIONS try { + #endif uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + #if __EXCEPTIONS } catch (...) { strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); return UNWIND_X86_64_MODE_DWARF; } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { @@ -1410,7 +1455,7 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo for (int i=0; i < 64; ++i) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { - sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + sprintf(warningBuffer, "register %d saved somewhere other than in frame", i); return UNWIND_X86_MODE_DWARF; } switch (i) { @@ -1554,12 +1599,22 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo if ( stackValue > stackMaxImmedValue ) { // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; - uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); - stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + #if __EXCEPTIONS + try { + #endif + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + #if __EXCEPTIONS + } + catch (...) { + strcpy(warningBuffer, "stack size is large but stack subl instruction not found"); + return UNWIND_X86_MODE_DWARF; + } + #endif stackValue = functionContentAdjustStackIns - funcAddr; immedStackSize = false; if ( stackAdjust > 7 ) { - strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + strcpy(warningBuffer, "stack subl instruction is too different from dwarf stack size"); return UNWIND_X86_MODE_DWARF; } encoding = UNWIND_X86_MODE_STACK_IND; @@ -1715,6 +1770,195 @@ compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlo +// +// arm64 specific functions +// + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_arm64&) +{ + return UNWIND_ARM64_MODE_DWARF; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_arm64&) +{ + return (regNum == UNW_ARM64_LR); +} + +template +int DwarfInstructions::lastRestoreReg(const Registers_arm64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_ARM64_D31 ); + return UNW_ARM64_D31; +} + + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_arm64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else + ABORT("getCFA(): unsupported location for arm64 cfa"); +} + +template +bool DwarfInstructions::checkRegisterPair(uint32_t reg, const typename CFI_Parser::PrologInfo& prolog, + int& offset, char warningBuffer[1024]) +{ + if ( (prolog.savedRegisters[reg].location != CFI_Parser::kRegisterUnused) + || (prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterUnused) ) { + if ( prolog.savedRegisters[reg].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg); + return false; + } + if ( prolog.savedRegisters[reg+1].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other than in frame", reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != prolog.savedRegisters[reg+1].value + 8 ) { + sprintf(warningBuffer, "registers %d and %d not saved contiguously in frame", reg, reg+1); + return false; + } + if ( prolog.savedRegisters[reg].value != offset ) { + sprintf(warningBuffer, "registers %d not saved contiguously in frame", reg); + return false; + } + offset -= 16; + return true; + } + return false; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_arm64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == UNW_ARM64_LR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_ARM64_MODE_DWARF; + } + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_ARM64_MODE_DWARF; + } + + compact_unwind_encoding_t encoding = 0; + int offset = 0; + + // figure out which kind of frame this function uses + bool standardFPframe = ( + (prolog.cfaRegister == UNW_ARM64_FP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_ARM64_FP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_FP].value == -16) + && (prolog.savedRegisters[UNW_ARM64_LR].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_ARM64_LR].value == -8) ); + + bool standardFrameless = ( prolog.cfaRegister == UNW_ARM64_SP ); + + if ( standardFrameless ) { + // verify enough space for registers saved + int count = 0; + for (int i=0; i < 96; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) + ++count; + } + if ( count * 8 > prolog.cfaRegisterOffset ) { + strcpy(warningBuffer, "saved registers do not fit in stack size"); + return UNWIND_ARM64_MODE_DWARF; + } + if ( (prolog.cfaRegisterOffset % 16) != 0 ) { + strcpy(warningBuffer, "stack size is not 16-byte multiple"); + return UNWIND_ARM64_MODE_DWARF; + } + const int32_t maxStack = (UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK >> __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + if ( (prolog.cfaRegisterOffset / 16) > maxStack ) { + strcpy(warningBuffer, "stack size is too large for frameless function"); + return UNWIND_ARM64_MODE_DWARF; + } + encoding = UNWIND_ARM64_MODE_FRAMELESS | ((prolog.cfaRegisterOffset/16) << __builtin_ctz(UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)); + offset = -16; + } + else if ( standardFPframe ) { + encoding = UNWIND_ARM64_MODE_FRAME; + offset = -24; + } + else { + // no compact encoding for this + strcpy(warningBuffer, "does not use standard frame"); + return UNWIND_ARM64_MODE_DWARF; + } + + // make sure no volatile registers are saved + for (int i=UNW_ARM64_X0; i < UNW_ARM64_X19; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_SP+1; i < UNW_ARM64_D8; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + for (int i=UNW_ARM64_D16; i < UNW_ARM64_D31+1; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + sprintf(warningBuffer, "non standard register %d saved in frame", i); + return UNWIND_ARM64_MODE_DWARF; + } + } + + // compute encoding + bool X19_X20_saved = checkRegisterPair(UNW_ARM64_X19, prolog, offset, warningBuffer); + bool X21_X22_saved = checkRegisterPair(UNW_ARM64_X21, prolog, offset, warningBuffer); + bool X23_X24_saved = checkRegisterPair(UNW_ARM64_X23, prolog, offset, warningBuffer); + bool X25_X26_saved = checkRegisterPair(UNW_ARM64_X25, prolog, offset, warningBuffer); + bool X27_X28_saved = checkRegisterPair(UNW_ARM64_X27, prolog, offset, warningBuffer); + bool D8_D9_saved = checkRegisterPair(UNW_ARM64_D8, prolog, offset, warningBuffer); + bool D10_D11_saved = checkRegisterPair(UNW_ARM64_D10, prolog, offset, warningBuffer); + bool D12_D13_saved = checkRegisterPair(UNW_ARM64_D12, prolog, offset, warningBuffer); + bool D14_D15_saved = checkRegisterPair(UNW_ARM64_D14, prolog, offset, warningBuffer); + if ( warningBuffer[0] != '\0' ) + return UNWIND_ARM64_MODE_DWARF; + + if ( X19_X20_saved ) + encoding |= UNWIND_ARM64_FRAME_X19_X20_PAIR; + if ( X21_X22_saved ) + encoding |= UNWIND_ARM64_FRAME_X21_X22_PAIR; + if ( X23_X24_saved ) + encoding |= UNWIND_ARM64_FRAME_X23_X24_PAIR; + if ( X25_X26_saved ) + encoding |= UNWIND_ARM64_FRAME_X25_X26_PAIR; + if ( X27_X28_saved ) + encoding |= UNWIND_ARM64_FRAME_X27_X28_PAIR; + if ( D8_D9_saved ) + encoding |= UNWIND_ARM64_FRAME_D8_D9_PAIR; + if ( D10_D11_saved ) + encoding |= UNWIND_ARM64_FRAME_D10_D11_PAIR; + if ( D12_D13_saved ) + encoding |= UNWIND_ARM64_FRAME_D12_D13_PAIR; + if ( D14_D15_saved ) + encoding |= UNWIND_ARM64_FRAME_D14_D15_PAIR; + + return encoding; +} } // namespace libunwind diff --git a/ld64/src/ld/parsers/libunwind/DwarfParser.hpp b/ld64/src/ld/parsers/libunwind/DwarfParser.hpp index 3824d2e..2e4a3bb 100644 --- a/ld64/src/ld/parsers/libunwind/DwarfParser.hpp +++ b/ld64/src/ld/parsers/libunwind/DwarfParser.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * diff --git a/ld64/src/ld/parsers/libunwind/Registers.hpp b/ld64/src/ld/parsers/libunwind/Registers.hpp index 7d39fd7..0247066 100644 --- a/ld64/src/ld/parsers/libunwind/Registers.hpp +++ b/ld64/src/ld/parsers/libunwind/Registers.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2007-2009 Apple Inc. All rights reserved. + * Copyright (c) 2007-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -1035,9 +1035,288 @@ inline const char* Registers_ppc::getRegisterName(int regNum) return "unknown register"; } +} + + + + + +struct arm_thread_state64_t +{ + __uint64_t __x[29]; /* General purpose registers x0-x28 */ + __uint64_t __fp; /* Frame pointer x29 */ + __uint64_t __lr; /* Link register x30 */ + __uint64_t __sp; /* Stack pointer x31 */ + __uint64_t __pc; /* Program counter */ + __uint32_t __cpsr; /* Current program status register */ + __uint32_t padding[3]; /* round up struct size to be 0x110 */ +}; + + +/// +/// Registers_arm64 holds the register state of a thread in a 64-bit intel process. +/// +class Registers_arm64 +{ +public: + Registers_arm64(); + Registers_arm64(const void* registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto(); + uint64_t getSP() const { return fRegisters.__sp; } + void setSP(uint64_t value) { fRegisters.__sp = value; } + uint64_t getIP() const { return fRegisters.__pc; } + void setIP(uint64_t value) { fRegisters.__pc = value; } + uint64_t getFP() const { return fRegisters.__fp; } + void setFP(uint64_t value) { fRegisters.__fp = value; } +private: + arm_thread_state64_t fRegisters; + double fHalfVectorRegisters[32]; + // Currently only the lower double in 128-bit vectore registers + // is perserved during unwinding. We could define new register + // numbers (> 96) which mean whole vector registers, then this + // struct would need to change to contain whole vector registers. +}; + +inline Registers_arm64::Registers_arm64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_arm64) < sizeof(unw_context_t) ); + fRegisters = *((arm_thread_state64_t*)registers); +} + +inline Registers_arm64::Registers_arm64() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fRegisters, sizeof(fHalfVectorRegisters)); +} + + +inline bool Registers_arm64::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 95 ) + return false; + if ( (regNum > 31) && (regNum < 64) ) + return false; + return true; +} + +inline uint64_t Registers_arm64::getRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return fRegisters.__pc; + if ( regNum == UNW_REG_SP ) + return fRegisters.__sp; + if ( (regNum >= 0) && (regNum < 32) ) + return fRegisters.__x[regNum]; + ABORT("unsupported arm64 register"); +} + +inline void Registers_arm64::setRegister(int regNum, uint64_t value) +{ + if ( regNum == UNW_REG_IP ) + fRegisters.__pc = value; + else if ( regNum == UNW_REG_SP ) + fRegisters.__sp = value; + else if ( (regNum >= 0) && (regNum < 32) ) + fRegisters.__x[regNum] = value; + else + ABORT("unsupported arm64 register"); +} + +inline const char* Registers_arm64::getRegisterName(int regNum) +{ + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_ARM64_X0: + return "x0"; + case UNW_ARM64_X1: + return "x1"; + case UNW_ARM64_X2: + return "x2"; + case UNW_ARM64_X3: + return "x3"; + case UNW_ARM64_X4: + return "x4"; + case UNW_ARM64_X5: + return "x5"; + case UNW_ARM64_X6: + return "x6"; + case UNW_ARM64_X7: + return "x7"; + case UNW_ARM64_X8: + return "x8"; + case UNW_ARM64_X9: + return "x9"; + case UNW_ARM64_X10: + return "x10"; + case UNW_ARM64_X11: + return "x11"; + case UNW_ARM64_X12: + return "x12"; + case UNW_ARM64_X13: + return "x13"; + case UNW_ARM64_X14: + return "x14"; + case UNW_ARM64_X15: + return "x15"; + case UNW_ARM64_X16: + return "x16"; + case UNW_ARM64_X17: + return "x17"; + case UNW_ARM64_X18: + return "x18"; + case UNW_ARM64_X19: + return "x19"; + case UNW_ARM64_X20: + return "x20"; + case UNW_ARM64_X21: + return "x21"; + case UNW_ARM64_X22: + return "x22"; + case UNW_ARM64_X23: + return "x23"; + case UNW_ARM64_X24: + return "x24"; + case UNW_ARM64_X25: + return "x25"; + case UNW_ARM64_X26: + return "x26"; + case UNW_ARM64_X27: + return "x27"; + case UNW_ARM64_X28: + return "x28"; + case UNW_ARM64_X29: + return "fp"; + case UNW_ARM64_X30: + return "lr"; + case UNW_ARM64_X31: + return "sp"; + case UNW_ARM64_D0: + return "d0"; + case UNW_ARM64_D1: + return "d1"; + case UNW_ARM64_D2: + return "d2"; + case UNW_ARM64_D3: + return "d3"; + case UNW_ARM64_D4: + return "d4"; + case UNW_ARM64_D5: + return "d5"; + case UNW_ARM64_D6: + return "d6"; + case UNW_ARM64_D7: + return "d7"; + case UNW_ARM64_D8: + return "d8"; + case UNW_ARM64_D9: + return "d9"; + case UNW_ARM64_D10: + return "d10"; + case UNW_ARM64_D11: + return "d11"; + case UNW_ARM64_D12: + return "d12"; + case UNW_ARM64_D13: + return "d13"; + case UNW_ARM64_D14: + return "d14"; + case UNW_ARM64_D15: + return "d15"; + case UNW_ARM64_D16: + return "d16"; + case UNW_ARM64_D17: + return "d17"; + case UNW_ARM64_D18: + return "d18"; + case UNW_ARM64_D19: + return "d19"; + case UNW_ARM64_D20: + return "d20"; + case UNW_ARM64_D21: + return "d21"; + case UNW_ARM64_D22: + return "d22"; + case UNW_ARM64_D23: + return "d23"; + case UNW_ARM64_D24: + return "d24"; + case UNW_ARM64_D25: + return "d25"; + case UNW_ARM64_D26: + return "d26"; + case UNW_ARM64_D27: + return "d27"; + case UNW_ARM64_D28: + return "d28"; + case UNW_ARM64_D29: + return "d29"; + case UNW_ARM64_D30: + return "d30"; + case UNW_ARM64_D31: + return "d31"; + default: + return "unknown register"; + } +} + +bool Registers_arm64::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_ARM64_D0 ) + return false; + if ( regNum > UNW_ARM64_D31 ) + return false; + return true; +} + +double Registers_arm64::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fHalfVectorRegisters[regNum-UNW_ARM64_D0]; +} + +void Registers_arm64::setFloatRegister(int regNum, double value) +{ + assert(validFloatRegister(regNum)); + fHalfVectorRegisters[regNum-UNW_ARM64_D0] = value; } +inline bool Registers_arm64::validVectorRegister(int regNum) const +{ + return false; +} + +inline v128 Registers_arm64::getVectorRegister(int regNum) const +{ + ABORT("no arm64 vector register support yet"); +} + +inline void Registers_arm64::setVectorRegister(int regNum, v128 value) +{ + ABORT("no arm64 vector register support yet"); +} + + } // namespace libunwind diff --git a/ld64/src/ld/parsers/lto_file.cpp b/ld64/src/ld/parsers/lto_file.cpp index 023f6e3..3fe21f6 100644 --- a/ld64/src/ld/parsers/lto_file.cpp +++ b/ld64/src/ld/parsers/lto_file.cpp @@ -47,7 +47,6 @@ #define __STDC_CONSTANT_MACROS 1 #include "llvm-c/lto.h" - namespace lto { @@ -106,6 +105,8 @@ 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; } @@ -197,7 +198,8 @@ public: static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength); static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, + bool logAllFiles, bool verboseOptimizationHints); static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } static bool optimize( const std::vector& allAtoms, ld::Internal& state, @@ -211,6 +213,9 @@ public: private: static const char* tripletPrefixForArch(cpu_type_t arch); static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options); +#if LTO_API_VERSION >= 7 + static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t, const char*, void*); +#endif typedef std::unordered_set CStringSet; typedef std::unordered_map CStringToAtom; @@ -274,7 +279,7 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) } File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints) { File* f = new File(path, modTime, ordinal, fileContent, fileLength, architecture); _s_files.push_back(f); @@ -290,7 +295,11 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.architecture = options.arch; objOpts.objSubtypeMustMatch = false; objOpts.logAllFiles = false; - objOpts.convertUnwindInfo = true; + objOpts.warnUnwindConversionProblems = options.needsUnwindInfoSection; + objOpts.keepDwarfUnwind = options.keepDwarfUnwind; + objOpts.forceDwarfConversion = false; + objOpts.neverConvertDwarf = false; + objOpts.verboseOptimizationHints = options.verboseOptimizationHints; objOpts.subType = 0; // mach-o parsing is done in-memory, but need path for debug notes @@ -447,6 +456,30 @@ void Atom::setCompiledAtom(const ld::Atom& atom) +// The order that files are merged must match command line order +struct CommandLineOrderFileSorter +{ + bool operator()(File* left, File* right) + { + return ( left->ordinal() < right->ordinal() ); + } +}; + + +#if LTO_API_VERSION >= 7 +void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, const char* message, void*) +{ + switch ( severity ) { + case LTO_DS_NOTE: + case LTO_DS_WARNING: + warning("%s", message); + break; + case LTO_DS_ERROR: + throwf("%s", message); + } +} +#endif + bool Parser::optimize( const std::vector& allAtoms, ld::Internal& state, const OptimizeOptions& options, @@ -469,10 +502,20 @@ bool Parser::optimize( const std::vector& allAtoms, // create optimizer and add each Reader lto_code_gen_t generator = ::lto_codegen_create(); +#if LTO_API_VERSION >= 7 + lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL); +#endif + + // The order that files are merged must match command line order + std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); + ld::File::Ordinal lastOrdinal; for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", (*it)->path()); - if ( ::lto_codegen_add_module(generator, (*it)->module()) ) - throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", (*it)->path(), ::lto_get_error_message(), ::lto_get_version()); + File* f = *it; + assert(f->ordinal() > lastOrdinal); + if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); + if ( ::lto_codegen_add_module(generator, f->module()) ) + throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); + lastOrdinal = f->ordinal(); } // add any -mllvm command line options @@ -481,6 +524,10 @@ bool Parser::optimize( const std::vector& allAtoms, ::lto_codegen_debug_options(generator, *it); } + // Need a way for LTO to get cpu variants (until that info is in bitcode) + if ( options.mcpu != NULL ) + ::lto_codegen_set_cpu(generator, options.mcpu); + // The atom graph uses directed edges (references). Collect all references where // originating atom is not part of any LTO Reader. This allows optimizer to optimize an // external (i.e. not originated from same .o file) reference if all originating atoms are also @@ -505,10 +552,7 @@ bool Parser::optimize( const std::vector& allAtoms, break; case ld::Fixup::bindingsIndirectlyBound: target = state.indirectBindingTable[fit->u.bindingIndex]; - if ( target == NULL ) - throwf("'%s' in %s contains undefined reference", atom->name(), atom->file()->path()); - assert(target != NULL); - if ( target->contentType() == ld::Atom::typeLTOtemporary ) + if ( (target != NULL) && (target->contentType() == ld::Atom::typeLTOtemporary) ) nonLLVMRefs.insert(target->name()); default: break; @@ -718,6 +762,8 @@ bool Parser::optimize( const std::vector& allAtoms, void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) { + static const ld::Atom* lastProxiedAtom = NULL; + static const ld::File* lastProxiedFile = NULL; // update proxy atoms to point to real atoms and find new atoms const char* name = machoAtom.name(); if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { @@ -725,6 +771,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) if ( pos != _llvmAtoms.end() ) { // turn Atom into a proxy for this mach-o atom pos->second->setCompiledAtom(machoAtom); + lastProxiedAtom = &machoAtom; + lastProxiedFile = pos->second->file(); } else { // an atom of this name was not in the allAtoms list the linker gave us @@ -750,6 +798,11 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) else { // ld only knew about non-static atoms, so this one must be new _newAtoms.push_back(&machoAtom); + // if new static atom in same section as previous non-static atom, assign to same file as previous + if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) { + ld::Atom* ma = const_cast(&machoAtom); + ma->setFile(lastProxiedFile); + } } // adjust fixups to go through proxy atoms @@ -775,7 +828,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) fit->u.target = pos->second; } else { - if ( _deadllvmAtoms.find(targetName) != _deadllvmAtoms.end() ) { + // Don't unbind follow-on reference into by-name reference + if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) { // target was coalesed away and replace by mach-o atom from a non llvm .o file fit->binding = ld::Fixup::bindingByNameUnbound; fit->u.name = targetName; @@ -819,11 +873,12 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, + bool verboseOptimizationHints) { Mutex lock; if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints); else return NULL; } diff --git a/ld64/src/ld/parsers/lto_file.h b/ld64/src/ld/parsers/lto_file.h index 3503fa9..d75aab3 100644 --- a/ld64/src/ld/parsers/lto_file.h +++ b/ld64/src/ld/parsers/lto_file.h @@ -39,7 +39,8 @@ extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_ty extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, + bool verboseOptimizationHints); struct OptimizeOptions { const char* outputFilePath; @@ -53,7 +54,11 @@ struct OptimizeOptions { bool relocatable; bool allowTextRelocs; bool linkerDeadStripping; + bool needsUnwindInfoSection; + bool keepDwarfUnwind; + bool verboseOptimizationHints; cpu_type_t arch; + const char* mcpu; const std::vector* llvmOptions; }; diff --git a/ld64/src/ld/parsers/macho_dylib_file.cpp b/ld64/src/ld/parsers/macho_dylib_file.cpp index aad1a8b..d37098b 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.cpp +++ b/ld64/src/ld/parsers/macho_dylib_file.cpp @@ -1,3 +1,4 @@ + /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2005-2011 Apple Inc. All rights reserved. @@ -142,7 +143,7 @@ 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, @@ -201,11 +204,13 @@ private: void buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, const macho_nlist

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

* header = (const macho_header

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

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

* )cmd; @@ -355,6 +369,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // }; // #define OBJC_IMAGE_SUPPORTS_GC 2 // #define OBJC_IMAGE_GC_ONLY 4 + // #define OBJC_IMAGE_IS_SIMULATED 32 // const uint32_t* contents = (uint32_t*)(&fileContent[sect->offset()]); if ( (sect->size() >= 8) && (contents[0] == 0) ) { @@ -363,6 +378,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, _objcContraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) _objcContraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( (flags & 32) == 32 ) + _objcContraint = ld::File::objcConstraintRetainReleaseForSimulator; else _objcContraint = ld::File::objcConstraintRetainRelease; } @@ -499,6 +516,28 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, munmap((caddr_t)fileContent, fileLength); } +// +// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz +// +template +uint32_t File::parseVersionNumber32(const char* versionString) +{ + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + char* end; + x = strtoul(versionString, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (*end != '\0') || (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed 32-bit x.y.z version number: %s", versionString); + + return (x << 16) | ( y << 8 ) | z; +} template void File::buildExportHashTableFromSymbolTable(const macho_dysymtab_command

* dynamicInfo, @@ -605,6 +644,14 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address } else if ( strncmp(symAction, "install_name$", 13) == 0 ) { _dylibInstallPath = symName; + _installPathOverride = true; + // CoreGraphics redirects to ApplicationServices, but with wrong compat version + if ( strcmp(_dylibInstallPath, "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices") == 0 ) + _dylibCompatibilityVersion = parseVersionNumber32("1.0"); + return; + } + else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { + _dylibCompatibilityVersion = parseVersionNumber32(symName); return; } else { @@ -777,6 +824,9 @@ bool File::isPublicLocation(const char* pth) template void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) { + // only do this once + if ( _indirectDylibsProcessed ) + return; const static bool log = false; if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); if ( _linkingFlat ) { @@ -794,7 +844,7 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b if ( log ) fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), it->path); // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child it->dylib = (File*)handler->findDylib(it->path, this->path()); - if ( it->dylib->hasPublicInstallName() ) { + if ( it->dylib->hasPublicInstallName() && !it->dylib->wrongOS() ) { // promote this child to be automatically added as a direct dependent if this already is if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(it->path,it->dylib->installPath()) == 0) ) { if ( log ) fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", it->dylib->installPath()); @@ -834,6 +884,8 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b chain.prev = NULL; chain.file = this; this->assertNoReExportCycles(&chain); + + _indirectDylibsProcessed = true; } template @@ -866,6 +918,7 @@ 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 +928,7 @@ public: opts.implicitlyLinkIndirectPublicDylibs(), opts.macosxVersionMin(), opts.iOSVersionMin(), + opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), opts.logAllFiles(), opts.installPath(), @@ -968,6 +1022,146 @@ bool Parser::validFile(const uint8_t* fileContent, bool executableOrDylibor +template <> +bool Parser::validFile(const uint8_t* fileContent, bool executableOrDyliborBundle) +{ + const macho_header

* header = (const macho_header

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

* header = (const macho_header

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

* header = (const macho_header

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

* header = (const macho_header

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

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC_64 ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM64 ) + return NULL; + return "arm64"; +} +#endif + +// +// used by linker is error messages to describe mismatched files +// +const char* archName(const uint8_t* fileContent) +{ + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } + if ( Parser::validFile(fileContent, true) ) { + return Parser::fileKind(fileContent); + } +#if SUPPORT_ARCH_arm64 + if ( Parser::validFile(fileContent, false) ) { + return Parser::fileKind(fileContent); + } +#endif + return NULL; +} + + // // main function used by linker to instantiate ld::Files // @@ -994,6 +1188,12 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( Parser::validFile(fileContent, bundleLoader) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif } return NULL; } diff --git a/ld64/src/ld/parsers/macho_dylib_file.h b/ld64/src/ld/parsers/macho_dylib_file.h index ad03af1..4d093f1 100644 --- a/ld64/src/ld/parsers/macho_dylib_file.h +++ b/ld64/src/ld/parsers/macho_dylib_file.h @@ -31,6 +31,12 @@ namespace mach_o { namespace dylib { + +extern bool isDylibFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult); + +extern const char* archName(const uint8_t* fileContent); + + extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, const Options& opts, ld::File::Ordinal ordinal, bool bundleLoader, bool indirectDylib); diff --git a/ld64/src/ld/parsers/macho_relocatable_file.cpp b/ld64/src/ld/parsers/macho_relocatable_file.cpp index b49d63f..ad5720e 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.cpp +++ b/ld64/src/ld/parsers/macho_relocatable_file.cpp @@ -92,9 +92,10 @@ 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: @@ -167,6 +170,7 @@ protected: Atom* findContentAtomByAddress(pint_t addr, class Atom* start, class Atom* end); uint32_t x86_64PcRelOffset(uint8_t r_type); + void addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]); static const char* makeSegmentName(const macho_section* s); static bool readable(const macho_section* s); static bool writable(const macho_section* s); @@ -235,7 +239,7 @@ 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 +278,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 +384,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 +487,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 +511,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 +539,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 +561,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 +584,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 +603,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 +641,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; } @@ -671,7 +677,7 @@ class Atom : public ld::Atom { public: // overrides of ld::Atom - virtual ld::File* file() const { return §().file(); } + virtual const ld::File* file() const; virtual const char* translationUnitSource() const { return sect().file().translationUnitSource(); } virtual const char* name() const { return _name; } @@ -689,6 +695,7 @@ public: virtual ld::Atom::UnwindInfo::iterator endUnwind() const { return &machofile()._unwindInfos[_unwindInfoStartIndex+_unwindInfoCount]; } virtual ld::Atom::LineInfo::iterator beginLineInfo() const{ return &machofile()._lineInfos[_lineInfoStartIndex]; } virtual ld::Atom::LineInfo::iterator endLineInfo() const { return &machofile()._lineInfos[_lineInfoStartIndex+_lineInfoCount]; } + virtual void setFile(const ld::File* f); private: @@ -714,7 +721,7 @@ public: throwf("too may fixups in %s", name()); ++_fixupsCount; } const uint8_t* contentPointer() const; uint32_t fixupCount() const { return _fixupsCount; } - void verifyAlignment() const; + void verifyAlignment(const macho_section&) const; typedef typename A::P P; typedef typename A::P::E E; @@ -748,7 +755,7 @@ public: if ( _scope == ld::Atom::scopeGlobal && (sym.n_desc() & (N_WEAK_DEF|N_WEAK_REF)) == (N_WEAK_DEF|N_WEAK_REF) ) this->setAutoHide(); - this->verifyAlignment(); + this->verifyAlignment(*sct.machoSection()); } private: @@ -768,10 +775,27 @@ private: _fixupsCount : kFixupCountBits, _lineInfoCount : kLineInfoCountBits, _unwindInfoCount : kUnwindInfoCountBits; - + + static std::map _s_fileOverride; }; +template +std::map Atom::_s_fileOverride; +template +void Atom::setFile(const ld::File* f) { + _s_fileOverride[this] = f; +} + +template +const ld::File* Atom::file() const +{ + std::map::iterator pos = _s_fileOverride.find(this); + if ( pos != _s_fileOverride.end() ) + return pos->second; + + return §().file(); +} template void Atom::setFixupsRange(uint32_t startIndex, uint32_t count) @@ -838,7 +862,7 @@ void Atom::copyRawContent(uint8_t buffer[]) const } template <> -void Atom::verifyAlignment() const +void Atom::verifyAlignment(const macho_section

&) const { if ( (this->section().type() == ld::Section::typeCode) && ! isThumb() ) { if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) ) @@ -846,8 +870,19 @@ void Atom::verifyAlignment() const } } +#if SUPPORT_ARCH_arm64 +template <> +void Atom::verifyAlignment(const macho_section

& sect) const +{ + if ( (this->section().type() == ld::Section::typeCode) && (sect.size() != 0) ) { + if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) ) + warning("arm64 function not 4-byte aligned: %s from %s", this->name(), this->file()->path()); + } +} +#endif + template -void Atom::verifyAlignment() const +void Atom::verifyAlignment(const macho_section

&) const { } @@ -865,7 +900,9 @@ 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, + opts.neverConvertDwarf, opts.verboseOptimizationHints); return p.parse(opts); } @@ -934,7 +971,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,11 +1008,19 @@ 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; } + bool verboseOptimizationHints() { return _verboseOptimizationHints; } + bool neverConvertDwarf() { return _neverConvertDwarf; } + macho_data_in_code_entry

* dataInCodeStart() { return _dataInCodeStart; } macho_data_in_code_entry

* dataInCodeEnd() { return _dataInCodeEnd; } + const uint8_t* optimizationHintsStart() { return _lohStart; } + const uint8_t* optimizationHintsEnd() { return _lohEnd; } + bool hasOptimizationHints() { return _lohStart != _lohEnd; } + void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target); void addFixups(const SourceLocation& src, ld::Fixup::Kind kind, const TargetDesc& target, const TargetDesc& picBase); @@ -989,7 +1034,7 @@ 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 +1095,9 @@ 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, bool neverConvertDwarf, bool verboseOptimizationHints); ld::relocatable::File* parse(const ParserOptions& opts); uint8_t loadCommandSizeMask(); bool parseLoadCommands(); @@ -1095,6 +1141,8 @@ private: bool _hasUUID; macho_data_in_code_entry

* _dataInCodeStart; macho_data_in_code_entry

* _dataInCodeEnd; + const uint8_t* _lohStart; + const uint8_t* _lohEnd; // filled in by parse() CFISection* _EHFrameSection; @@ -1106,8 +1154,12 @@ 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; + bool _neverConvertDwarf; + bool _verboseOptimizationHints; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1118,7 +1170,8 @@ 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, + bool neverConvertDwarf, bool verboseOptimizationHints) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), _symbols(NULL), _symbolCount(0), _strings(NULL), _stringsSize(0), @@ -1126,10 +1179,14 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _undefinedStartIndex(0), _undefinedEndIndex(0), _sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false), _dataInCodeStart(NULL), _dataInCodeEnd(NULL), + _lohStart(NULL), _lohEnd(NULL), _EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL), _tentativeDefinitionCount(0), _absoluteSymbolCount(0), _symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false), - _overlappingSymbols(false), _convertUnwindInfo(convertDUI), _hasDataInCodeLabels(false), + _overlappingSymbols(false), _warnUnwindConversionProblems(convertDUI), _hasDataInCodeLabels(false), + _keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion), + _neverConvertDwarf(neverConvertDwarf), + _verboseOptimizationHints(verboseOptimizationHints), _stubsSectionNum(0), _stubsMachOSection(NULL) { } @@ -1183,6 +1240,19 @@ bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, c } +template <> +bool Parser::validFile(const uint8_t* fileContent, bool subtypeMustMatch, cpu_subtype_t subtype) +{ + const macho_header

* header = (const macho_header

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

* header = (const macho_header

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

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

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

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

* loc = (macho_linker_option_command

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

* loh = (macho_linkedit_data_command

*)cmd; + _lohStart = _fileContent + loh->dataoff(); + _lohEnd = _fileContent + loh->dataoff() + loh->datasize(); + if ( _lohEnd > endOfFile ) + throw "LC_LINKER_OPTIMIZATION_HINTS table extends beyond end of file"; + } + break; default: if ( cmd->cmd() == macho_segment_command

::CMD ) { if ( segment != NULL ) @@ -1984,7 +2144,6 @@ void Parser::makeSortedSymbolsArray(uint32_t array[], const uint32_t sectionA } } - template void Parser::makeSections() { @@ -2034,6 +2193,7 @@ void Parser::makeSections() // }; // #define OBJC_IMAGE_SUPPORTS_GC 2 // #define OBJC_IMAGE_GC_ONLY 4 + // #define OBJC_IMAGE_IS_SIMULATED 32 // const uint32_t* contents = (uint32_t*)(_file->fileContent()+sect->offset()); if ( (sect->size() >= 8) && (contents[0] == 0) ) { @@ -2042,6 +2202,8 @@ void Parser::makeSections() _file->_objConstraint = ld::File::objcConstraintGC; else if ( (flags & 2) == 2 ) _file->_objConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( (flags & 32) == 32 ) + _file->_objConstraint = ld::File::objcConstraintRetainReleaseForSimulator; else _file->_objConstraint = ld::File::objcConstraintRetainRelease; if ( sect->size() > 8 ) { @@ -2463,8 +2625,14 @@ const char* Parser::scanSymbolTableForAddress(uint64_t addr) continue; // return with exact match - if ( sym.n_value() == addr ) - return nameFromSymbol(sym); + if ( sym.n_value() == addr ) { + const char* name = nameFromSymbol(sym); + if ( strncmp(name, "ltmp", 4) != 0 ) + return name; + // treat 'ltmp*' labels as close match + closestSymAddr = sym.n_value(); + closestSymName = name; + } // record closest seen so far if ( (sym.n_value() < addr) && ((sym.n_value() > closestSymAddr) || (closestSymName == NULL)) ) @@ -2519,6 +2687,29 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co case ld::Fixup::kindStoreThumbBranch22: firstKind = ld::Fixup::kindStoreTargetAddressThumbBranch22; break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Branch26: + firstKind = ld::Fixup::kindStoreTargetAddressARM64Branch26; + break; + case ld::Fixup::kindStoreARM64Page21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64Page21; + break; + case ld::Fixup::kindStoreARM64PageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64PageOff12; + break; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21; + break; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12; + break; + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21; + break; + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + firstKind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12; + break; +#endif default: combined = false; cl = ld::Fixup::k1of2; @@ -2537,6 +2728,10 @@ void Parser::addFixups(const SourceLocation& src, ld::Fixup::Kind setKind, co // backing string in CFStrings should always be direct addFixup(src, cl, firstKind, target.atom); } + else if ( (src.atom == target.atom) && (target.atom->combine() == ld::Atom::combineByName) ) { + // reference to self should always be direct + addFixup(src, cl, firstKind, target.atom); + } else { // change direct fixup to by-name fixup addFixup(src, cl, firstKind, false, target.atom->name()); @@ -2922,6 +3117,22 @@ bool Parser::skip_form(const uint8_t ** offset, const uint8_t * end, uint64_t sz = 4; break; + case DW_FORM_sec_offset: + sz = sizeof(typename A::P::uint_t); + break; + + case DW_FORM_exprloc: + sz = read_uleb128 (offset, end); + break; + + case DW_FORM_flag_present: + sz = 0; + break; + + case DW_FORM_ref_sig8: + sz = 8; + break; + default: return false; } @@ -3402,7 +3613,7 @@ bool Parser::read_comp_unit(const char ** name, const char ** comp_dir, return false; vers = A::P::E::get16(*(uint16_t*)di); - if (vers < 2 || vers > 3) + if (vers < 2 || vers > 4) /* DWARF version wrong for this code. Chances are we could continue anyway, but we don't know for sure. */ return false; @@ -3695,7 +3906,10 @@ template ld::Atom::Alignment Section::alignmentForAddress(pint_t addr) { const uint32_t sectionAlignment = this->_machOSection->align(); - return ld::Atom::Alignment(sectionAlignment, (addr % (1 << sectionAlignment))); + uint32_t modulus = (addr % (1 << sectionAlignment)); + if ( modulus > 0xFFFF ) + warning("alignment for symbol at address 0x%08llX in %s exceeds 2^16", (uint64_t)addr, this->file().path()); + return ld::Atom::Alignment(sectionAlignment, modulus); } template @@ -3723,7 +3937,7 @@ template void CFISection::warnFunc(void* ref, uint64_t funcAddr, const char* msg) { Parser* parser = (Parser*)ref; - if ( ! parser->convertUnwindInfo() ) + if ( ! parser->warnUnwindConversionProblems() ) return; if ( funcAddr != CFI_INVALID_ADDRESS ) { // atoms are not constructed yet, so scan symbol table for labels @@ -3741,6 +3955,12 @@ bool CFISection::needsRelocating() return true; } +template <> +bool CFISection::needsRelocating() +{ + return true; +} + template bool CFISection::needsRelocating() { @@ -3748,9 +3968,9 @@ bool CFISection::needsRelocating() } template <> -void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // copy __eh_frame data to buffer memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); @@ -3796,7 +4016,6 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, } } - // create ObjectAddressSpace object for use by libunwind OAS oas(*this, buffer); @@ -3804,6 +4023,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::parseCFIs( oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), cfiArray, count, (void*)&parser, warnFunc); if ( msg != NULL ) throwf("malformed __eh_frame section: %s", msg); @@ -3812,7 +4032,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // create ObjectAddressSpace object for use by libunwind OAS oas(*this, (uint8_t*)this->file().fileContent()+this->_machOSection->offset()); @@ -3821,6 +4041,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, const char* msg; msg = libunwind::DwarfInstructions::parseCFIs( oas, this->_machOSection->addr(), this->_machOSection->size(), + cuStarts, cuCount, parser.keepDwarfUnwind(), parser.forceDwarfConversion(), parser.neverConvertDwarf(), cfiArray, count, (void*)&parser, warnFunc); if ( msg != NULL ) throwf("malformed __eh_frame section: %s", msg); @@ -3832,12 +4053,76 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], - uint32_t count) + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // arm does not use zero cost exceptions assert(count == 0); } +template <> +void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, + libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) +{ + // copy __eh_frame data to buffer + memcpy(buffer, file().fileContent() + this->_machOSection->offset(), this->_machOSection->size()); + + // and apply relocations + const macho_relocation_info

* relocs = (macho_relocation_info

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

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

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

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

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

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

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

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

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

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

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

* relocs = (macho_relocation_info

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

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

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

)) == macho_compact_unwind_entry

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

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

)) == macho_compact_unwind_entry

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

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

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

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

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

* foundLabel; Atom* allocatedSpace; - while ( it.next(parser, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { + while ( it.next(parser, *this, sectNum, startAddr, endAddr, &foundAddr, &size, &foundLabel) ) { if ( foundLabel != NULL ) { + bool skip = false; pint_t labeledAtomSize = this->elementSizeAtAddress(foundAddr); allocatedSpace = (Atom*)p; if ( this->ignoreLabel(parser.nameFromSymbol(*foundLabel)) ) { - //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)foundAddr); - new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, + if ( size == 0 ) { + // + // a size of zero means there is another label at same location + // and we are supposed to ignore this label + skip = true; + } + else { + //fprintf(stderr, " 0x%08llX make annon, size=%lld\n", (uint64_t)foundAddr, (uint64_t)size); + new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, foundAddr), foundAddr, this->elementSizeAtAddress(foundAddr), this->definition(), this->combine(parser, foundAddr), this->scopeAtAddress(parser, foundAddr), this->contentType(), this->symbolTableInclusion(), this->dontDeadStrip(), false, false, this->alignmentForAddress(foundAddr)); + } } else { // make named atom for label //fprintf(stderr, " 0x%08llX make labeled\n", (uint64_t)foundAddr); new (allocatedSpace) Atom(*this, parser, *foundLabel, labeledAtomSize); } - ++count; - p += sizeof(Atom); - foundAddr += labeledAtomSize; - size -= labeledAtomSize; + if ( !skip ) { + ++count; + p += sizeof(Atom); + foundAddr += labeledAtomSize; + size -= labeledAtomSize; + } } // some number of anonymous atoms for (pint_t addr = foundAddr; addr < (foundAddr+size); addr += elementSizeAtAddress(addr) ) { // make anon atoms for area before label if ( this->useElementAt(parser, it, addr) ) { - //fprintf(stderr, " 0x%08llX make annon\n", (uint64_t)addr); + //fprintf(stderr, " 0x%08llX make annon, size=%lld\n", (uint64_t)addr, (uint64_t)elementSizeAtAddress(addr)); allocatedSpace = (Atom*)p; new (allocatedSpace) Atom(*this, this->unlabeledAtomName(parser, addr), addr, this->elementSizeAtAddress(addr), this->definition(), this->combine(parser, addr), this->scopeAtAddress(parser, addr), @@ -4606,11 +5023,12 @@ bool CStringSection::useElementAt(Parser& parser, struct Parser::LabelA } template -bool CStringSection::ignoreLabel(const char* label) +bool CStringSection::ignoreLabel(const char* label) const { return (label[0] == 'L') || (label[0] == 'l'); } + template Atom* CStringSection::findAtomByAddress(pint_t addr) { @@ -4665,6 +5083,12 @@ ld::Fixup::Kind NonLazyPointerSection::fixupKind() return ld::Fixup::kindStoreLittleEndian32; } +template <> +ld::Fixup::Kind NonLazyPointerSection::fixupKind() +{ + return ld::Fixup::kindStoreLittleEndian64; +} + template <> void NonLazyPointerSection::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -4832,7 +5256,11 @@ const uint8_t* CFStringSection::targetContent(const class Atom* atom, cons *ct = contentUTF16; *count = (targetAtom->size()+1)/2; // round up incase of buggy compiler that has only one trailing zero byte } - assert(target != NULL); + else { + *ct = contentUnknown; + *count = 0; + return NULL; + } return target->contentPointer(); } assert(0); @@ -4862,7 +5290,8 @@ unsigned long CFStringSection::contentHash(const class Atom* atom, const l } return hash; case contentUnknown: - return 0; + // For malformed CFStrings, hash to address of atom so they have unique hashes + return ULONG_MAX - (unsigned long)(atom); } return 0; } @@ -4891,6 +5320,12 @@ bool CFStringSection::canCoalesceWith(const class Atom* atom, const ld::At if ( thisType != rhsType ) return false; + if ( thisType == contentUnknown ) + return false; + + if ( rhsType == contentUnknown ) + return false; + // no need to compare content of pointers are already the same if ( cstringContent == rhsStringContent ) return true; @@ -5056,12 +5491,16 @@ const char* PointerToCStringSection::targetCString(const class Atom* atom, case ld::Fixup::bindingsIndirectlyBound: targetAtom = ind.indirectAtom(fit->u.bindingIndex); break; + case ld::Fixup::bindingDirectlyBound: + targetAtom = fit->u.target; + break; default: - assert(0); + assert(0 && "unsupported reference to selector"); } assert(targetAtom != NULL); const Atom* target = dynamic_cast*>(targetAtom); - assert(target != NULL); + assert(target != NULL); + assert(target->contentType() == ld::Atom::typeCString); return (char*)target->contentPointer(); } @@ -6054,8 +6493,314 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relocati #endif - - +#if SUPPORT_ARCH_arm64 +template <> +bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

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

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

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

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

* reloc) @@ -6146,6 +6891,72 @@ bool Objc1ClassReferences::addRelocFixup(class Parser& parser, const m return PointerToCStringSection::addRelocFixup(parser, reloc); } +#if SUPPORT_ARCH_arm64 +template <> +void Section::addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]) { + switch (kind) { + case LOH_ARM64_ADRP_ADRP: + case LOH_ARM64_ADRP_LDR: + case LOH_ARM64_ADRP_ADD: + case LOH_ARM64_ADRP_LDR_GOT: + if ( count != 2 ) + warning("arm64 Linker Optimiztion Hint %d has wrong number of arguments", kind); + break; + case LOH_ARM64_ADRP_ADD_LDR: + case LOH_ARM64_ADRP_LDR_GOT_LDR: + case LOH_ARM64_ADRP_ADD_STR: + case LOH_ARM64_ADRP_LDR_GOT_STR: + if ( count != 3 ) + warning("arm64 Linker Optimiztion Hint %d has wrong number of arguments", kind); + } + + // pick lowest address in tuple for use as offsetInAtom + uint64_t lowestAddress = addrs[0]; + for(int i=1; i < count; ++i) { + if ( addrs[i] < lowestAddress ) + lowestAddress = addrs[i]; + } + // verify all other address are in same atom + Atom* inAtom = parser.findAtomByAddress(lowestAddress); + const uint64_t atomStartAddr = inAtom->objectAddress(); + const uint64_t atomEndAddr = atomStartAddr + inAtom->size(); + for(int i=0; i < count; ++i) { + if ( (addrs[i] < atomStartAddr) || (addrs[i] >= atomEndAddr) ) { + warning("arm64 Linker Optimiztion Hint addresses are not in same atom: 0x%08llX and 0x%08llX", + lowestAddress, addrs[i]); + return; // skip this LOH + } + if ( (addrs[i] & 0x3) != 0 ) { + warning("arm64 Linker Optimiztion Hint address is not 4-byte aligned: 0x%08llX", addrs[i]); + return; // skip this LOH + } + if ( (addrs[i] - lowestAddress) > 0xFFFF ) { + if ( parser.verboseOptimizationHints() ) { + warning("arm64 Linker Optimiztion Hint addresses are too far apart: 0x%08llX and 0x%08llX", + lowestAddress, addrs[i]); + } + return; // skip this LOH + } + } + + // encoded kind, count, and address deltas in 64-bit addend + ld::Fixup::LOH_arm64 extra; + extra.addend = 0; + extra.info.kind = kind; + extra.info.count = count-1; + extra.info.delta1 = (addrs[0] - lowestAddress) >> 2; + extra.info.delta2 = (count > 1) ? ((addrs[1] - lowestAddress) >> 2) : 0; + extra.info.delta3 = (count > 2) ? ((addrs[2] - lowestAddress) >> 2) : 0; + extra.info.delta4 = (count > 3) ? ((addrs[3] - lowestAddress) >> 2) : 0; + typename Parser::SourceLocation src(inAtom, lowestAddress- inAtom->objectAddress()); + parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindLinkerOptimizationHint, extra.addend); +} +#endif + +template +void Section::addLOH(class Parser& parser, int kind, int count, const uint64_t addrs[]) { + +} template void Section::makeFixups(class Parser& parser, const struct Parser::CFI_CU_InfoArrays&) @@ -6261,6 +7072,42 @@ void Section::makeFixups(class Parser& parser, const struct Parser::CFI } } + // convert linker optimization hints into internal format + if ( this->type() == ld::Section::typeCode && parser.hasOptimizationHints() ) { + const pint_t startAddr = this->_machOSection->addr(); + const pint_t endAddr = startAddr + this->_machOSection->size(); + for (const uint8_t* p = parser.optimizationHintsStart(); p < parser.optimizationHintsEnd(); ) { + uint64_t addrs[4]; + int32_t kind = read_uleb128(&p, parser.optimizationHintsEnd()); + if ( kind == 0 ) // padding at end of loh buffer + break; + if ( kind == -1 ) { + warning("malformed uleb128 kind in LC_LINKER_OPTIMIZATION_HINTS"); + break; + } + int32_t count = read_uleb128(&p, parser.optimizationHintsEnd()); + if ( count == -1 ) { + warning("malformed uleb128 count in LC_LINKER_OPTIMIZATION_HINTS"); + break; + } + if ( count > 3 ) { + warning("address count > 3 in LC_LINKER_OPTIMIZATION_HINTS"); + break; + } + for (int32_t i=0; i < count; ++i) { + addrs[i] = read_uleb128(&p, parser.optimizationHintsEnd()); + } + if ( (startAddr <= addrs[0]) && (addrs[0] < endAddr) ) { + this->addLOH(parser, kind, count, addrs); + //fprintf(stderr, "kind=%d", kind); + //for (int32_t i=0; i < count; ++i) { + // fprintf(stderr, ", addr=0x%08llX", addrs[i]); + //} + //fprintf(stderr, "\n"); + } + } + } + // add follow-on fixups for aliases if ( _hasAliases ) { @@ -6303,6 +7150,12 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ) + return mach_o::relocatable::Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts); + break; +#endif } return NULL; } @@ -6319,6 +7172,8 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO return ( mach_o::relocatable::Parser::validFile(fileContent) ); case CPU_TYPE_ARM: return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); + case CPU_TYPE_ARM64: + return ( mach_o::relocatable::Parser::validFile(fileContent, opts.objSubtypeMustMatch, opts.subType) ); } return false; } @@ -6330,7 +7185,8 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* { if ( mach_o::relocatable::Parser::validFile(fileContent) ) { *result = CPU_TYPE_X86_64; - *subResult = CPU_SUBTYPE_X86_64_ALL; + const macho_header >* header = (const macho_header >*)fileContent; + *subResult = header->cpusubtype(); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent) ) { @@ -6344,6 +7200,11 @@ bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* *subResult = header->cpusubtype(); return true; } + if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + *result = CPU_TYPE_ARM64; + *subResult = CPU_SUBTYPE_ARM64_ALL; + return true; + } return false; } @@ -6378,6 +7239,11 @@ bool hasObjC2Categories(const uint8_t* fileContent) else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); } +#if SUPPORT_ARCH_arm64 + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::hasObjC2Categories(fileContent); + } +#endif return false; } diff --git a/ld64/src/ld/parsers/macho_relocatable_file.h b/ld64/src/ld/parsers/macho_relocatable_file.h index 021c3f2..6d20847 100644 --- a/ld64/src/ld/parsers/macho_relocatable_file.h +++ b/ld64/src/ld/parsers/macho_relocatable_file.h @@ -35,7 +35,11 @@ struct ParserOptions { uint32_t architecture; bool objSubtypeMustMatch; bool logAllFiles; - bool convertUnwindInfo; + bool warnUnwindConversionProblems; + bool keepDwarfUnwind; + bool forceDwarfConversion; + bool neverConvertDwarf; + bool verboseOptimizationHints; uint32_t subType; }; diff --git a/ld64/src/ld/passes/branch_island.cpp b/ld64/src/ld/passes/branch_island.cpp index 96b6d35..5f5612c 100644 --- a/ld64/src/ld/passes/branch_island.cpp +++ b/ld64/src/ld/passes/branch_island.cpp @@ -41,6 +41,7 @@ namespace passes { namespace branch_island { +static std::map sAtomToAddress; struct TargetAndOffset { const ld::Atom* atom; uint32_t offset; }; @@ -68,43 +69,27 @@ public: ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMBranch24, target), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%s: ARM jump instruction branch island to final target %s\n", + target->name(), finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const { - int64_t displacement = _target->finalAddress() - this->finalAddress() - 8; - if ( _target->contentType() == ld::Atom::typeBranchIsland ) { - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 8; - if ( (skipToFinalDisplacement < 33554428LL) && (skipToFinalDisplacement > (-33554432LL)) ) { - // can skip branch island and jump straight to target - if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); - displacement = skipToFinalDisplacement; - } - else { - // ultimate target is too far, jump to island - if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); - } - } - uint32_t imm24 = (displacement >> 2) & 0x00FFFFFF; - int32_t branchInstruction = 0xEA000000 | imm24; - OSWriteLittleInt32(buffer, 0, branchInstruction); + OSWriteLittleInt32(buffer, 0, 0xEA000000); } virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: const char* _name; - const ld::Atom* _target; - TargetAndOffset _finalTarget; + ld::Fixup _fixup1; + ld::Fixup _fixup2; }; @@ -154,62 +139,68 @@ public: ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), _name(nm), - _target(target), - _finalTarget(finalTarget) { } + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressThumbBranch22, target), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { + if (_s_log) fprintf(stderr, "%s: Thumb jump instruction branch island to final target %s\n", + target->name(), finalTarget.atom->name()); + } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _name; } virtual uint64_t size() const { return 4; } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const { - int64_t displacement = _target->finalAddress() - this->finalAddress() - 4; - if ( _target->contentType() == ld::Atom::typeBranchIsland ) { - // an ARM branch can branch farther than a thumb branch. The branch - // island generation was conservative and put islands every thumb - // branch distance apart. Check to see if this is a an island - // hopping branch that could be optimized to go directly to target. - int64_t skipToFinalDisplacement = _finalTarget.atom->finalAddress() + _finalTarget.offset - this->finalAddress() - 4; - if ( (skipToFinalDisplacement < 16777214) && (skipToFinalDisplacement > (-16777216LL)) ) { - // can skip branch island and jump straight to target - if (_s_log) fprintf(stderr, "%s: optimized jump to final target at 0x%08llX, thisAddr=0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress(), this->finalAddress()); - displacement = skipToFinalDisplacement; - } - else { - // ultimate target is too far for thumb2 branch, jump to island - if (_s_log) fprintf(stderr, "%s: jump to branch island at 0x%08llX\n", - _target->name(), _finalTarget.atom->finalAddress()); - } - } - // The instruction is really two instructions: - // The lower 16 bits are the first instruction, which contains the high - // 11 bits of the displacement. - // The upper 16 bits are the second instruction, which contains the low - // 11 bits of the displacement, as well as differentiating bl and blx. - uint32_t s = (uint32_t)(displacement >> 24) & 0x1; - uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; - uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; - uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; - uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; - uint32_t j1 = (i1 == s); - uint32_t j2 = (i2 == s); - uint32_t opcode = 0x9000F000; - uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; - uint32_t firstDisp = (s << 10) | imm10; - uint32_t newInstruction = opcode | (nextDisp << 16) | firstDisp; - //warning("s=%d, j1=%d, j2=%d, imm10=0x%0X, imm11=0x%0X, opcode=0x%08X, first=0x%04X, next=0x%04X, new=0x%08X, disp=0x%llX for %s to %s\n", - // s, j1, j2, imm10, imm11, opcode, firstDisp, nextDisp, newInstruction, displacement, inAtom->getDisplayName(), ref->getTarget().getDisplayName()); - OSWriteLittleInt32(buffer, 0, newInstruction); + OSWriteLittleInt32(buffer, 0, 0xf0008000); } virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: const char* _name; - const ld::Atom* _target; - TargetAndOffset _finalTarget; + ld::Fixup _fixup1; + ld::Fixup _fixup2; +}; + + + +class Thumb2toThumbBranchAbsoluteIslandAtom : public ld::Atom { +public: + Thumb2toThumbBranchAbsoluteIslandAtom(const char* nm, const ld::Section& inSect, TargetAndOffset finalTarget) + : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeBranchIsland, + ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)), + _name(nm), + _fixup1(0, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom), + _fixup2(0, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbLow16), + _fixup3(4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetAddress, finalTarget.atom), + _fixup4(4, ld::Fixup::k2of2, ld::Fixup::kindStoreThumbHigh16), + _fixup5(0, ld::Fixup::k1of1, ld::Fixup::kindIslandTarget, finalTarget.atom) { } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 10; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x0c00f240); // movw r12, #0x5678 + OSWriteLittleInt32(&buffer[4], 0, 0x0c00f2c0); // movt r12, #0x1234 + OSWriteLittleInt16(&buffer[8], 0, 0x4760); // bx r12 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup5)[1]; } + +private: + const char* _name; + ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; }; + class NoPicARMtoThumbMBranchIslandAtom : public ld::Atom { public: NoPicARMtoThumbMBranchIslandAtom(const char* nm, const ld::Atom* target, TargetAndOffset finalTarget) @@ -245,7 +236,8 @@ private: }; -static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, TargetAndOffset finalTarget) +static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int islandRegion, const ld::Atom* nextTarget, + TargetAndOffset finalTarget, const ld::Section& inSect, bool crossSectionBranch) { char* name; if ( finalTarget.offset == 0 ) { @@ -263,7 +255,10 @@ static ld::Atom* makeBranchIsland(const Options& opts, ld::Fixup::Kind kind, int case ld::Fixup::kindStoreThumbBranch22: case ld::Fixup::kindStoreTargetAddressARMBranch24: case ld::Fixup::kindStoreTargetAddressThumbBranch22: - if ( finalTarget.atom->isThumb() ) { + if ( crossSectionBranch && opts.preferSubArchitecture() && opts.archSupportsThumb2() ) { + return new Thumb2toThumbBranchAbsoluteIslandAtom(name, inSect, finalTarget); + } + else if ( finalTarget.atom->isThumb() ) { if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) { return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget); } @@ -343,47 +338,51 @@ static uint64_t maxDistanceBetweenIslands(const Options& opts, bool seenThumbBra // before any branches could be pushed out of range. // -void doPass(const Options& opts, ld::Internal& state) -{ - // only make branch islands in final linked images - if ( opts.outputKind() == Options::kObjectFile ) - return; - // only ARM needs branch islands - switch ( opts.architecture() ) { - case CPU_TYPE_ARM: - break; - default: - return; - } - - // scan to find __text section - ld::Internal::FinalSection* textSection = NULL; - for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { - ld::Internal::FinalSection* sect = *sit; - if ( strcmp(sect->sectionName(), "__text") == 0 ) - textSection = sect; - } - if ( textSection == NULL ) - return; - +static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld::Internal::FinalSection* textSection) +{ // assign section offsets to each atom in __text section, watch for thumb branches, and find total size - const bool isARM = (opts.architecture() == CPU_TYPE_ARM); bool hasThumbBranches = false; + bool haveCrossSectionBranches = false; + const bool preload = (opts.outputKind() == Options::kPreload); uint64_t offset = 0; for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) { const ld::Atom* atom = *ait; - // check for thumb branches - if ( isARM && ~hasThumbBranches ) { - for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { - switch ( fit->kind ) { - case ld::Fixup::kindStoreThumbBranch22: - case ld::Fixup::kindStoreTargetAddressThumbBranch22: - hasThumbBranches = true; - break; - default: - break; - } + // check for thumb branches and cross section branches + const ld::Atom* target = NULL; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = state.indirectBindingTable[fit->u.bindingIndex]; + break; + } + bool haveBranch = false; + switch (fit->kind) { + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + hasThumbBranches = true; + // fall into arm branch case + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + haveBranch = true; + break; + default: + break; + } + if ( haveBranch && (target->contentType() != ld::Atom::typeStub) ) { + // haveCrossSectionBranches only applies to -preload builds + if ( preload && (atom->section() != target->section()) ) + haveCrossSectionBranches = true; } } // align atom @@ -400,9 +399,9 @@ void doPass(const Options& opts, ld::Internal& state) offset += atom->size(); } uint64_t totalTextSize = offset; - if ( totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches) ) + if ( (totalTextSize < textSizeWhenMightNeedBranchIslands(opts, hasThumbBranches)) && !haveCrossSectionBranches ) return; - if (_s_log) fprintf(stderr, "ld: __text section size=%llu, might need branch islands\n", totalTextSize); + if (_s_log) fprintf(stderr, "ld: section %s size=%llu, might need branch islands\n", textSection->sectionName(), totalTextSize); // Figure out how many regions of branch islands will be needed, and their locations. // Construct a vector containing the atoms after which branch islands will be inserted, @@ -410,12 +409,12 @@ void doPass(const Options& opts, ld::Internal& state) const uint64_t kBetweenRegions = maxDistanceBetweenIslands(opts, hasThumbBranches); // place regions of islands every 14MB in __text section std::vector branchIslandInsertionPoints; // atoms in the atom list after which branch islands will be inserted uint64_t previousIslandEndAddr = 0; - const ld::Atom *insertionPoint; + const ld::Atom *insertionPoint = NULL; branchIslandInsertionPoints.reserve(totalTextSize/kBetweenRegions*2); for (std::vector::iterator it=textSection->atoms.begin(); it != textSection->atoms.end(); it++) { const ld::Atom* atom = *it; // if we move past the next atom, will the run length exceed kBetweenRegions? - if ( atom->sectionOffset() + atom->size() - previousIslandEndAddr > kBetweenRegions ) { + if ( atom->sectionOffset() + atom->size() > previousIslandEndAddr + kBetweenRegions ) { // yes. Add the last known good location (atom) for inserting a branch island. if ( insertionPoint == NULL ) throwf("Unable to insert branch island. No insertion point available."); @@ -427,28 +426,24 @@ void doPass(const Options& opts, ld::Internal& state) if ( !atom->hasFixupsOfKind(ld::Fixup::kindNoneFollowOn) ) insertionPoint = atom; } - // add one more island after the last atom - if (insertionPoint != NULL) + // add one more island after the last atom if close to limit + if ( (insertionPoint != NULL) && (insertionPoint->sectionOffset() + insertionPoint->size() > previousIslandEndAddr + (kBetweenRegions-0x100000)) ) branchIslandInsertionPoints.push_back(insertionPoint); - const int kIslandRegionsCount = branchIslandInsertionPoints.size(); - if (_s_log) { - fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); - for (std::vector::iterator it = branchIslandInsertionPoints.begin(); it != branchIslandInsertionPoints.end(); ++it) { - const ld::Atom* atom = *it; - const ld::File *file = atom->file(); - fprintf(stderr, "ld: branch island will be inserted at 0x%llx after %s", atom->sectionOffset()+atom->size(), atom->name()); - if (file) fprintf(stderr, " (%s)", atom->file()->path()); - fprintf(stderr, "\n"); - } + if ( haveCrossSectionBranches && branchIslandInsertionPoints.empty() ) { + branchIslandInsertionPoints.push_back(textSection->atoms.back()); } + const int kIslandRegionsCount = branchIslandInsertionPoints.size(); - + if (_s_log) fprintf(stderr, "ld: will use %u branch island regions\n", kIslandRegionsCount); typedef std::map AtomToIsland; AtomToIsland* regionsMap[kIslandRegionsCount]; + uint64_t regionAddresses[kIslandRegionsCount]; std::vector* regionsIslands[kIslandRegionsCount]; for(int i=0; i < kIslandRegionsCount; ++i) { regionsMap[i] = new AtomToIsland(); regionsIslands[i] = new std::vector(); + regionAddresses[i] = branchIslandInsertionPoints[i]->sectionOffset() + branchIslandInsertionPoints[i]->size(); + if (_s_log) fprintf(stderr, "ld: branch islands will be inserted at 0x%08llX after %s\n", regionAddresses[i], branchIslandInsertionPoints[i]->name()); } unsigned int islandCount = 0; @@ -493,25 +488,51 @@ void doPass(const Options& opts, ld::Internal& state) break; } if ( haveBranch ) { + bool crossSectionBranch = ( preload && (atom->section() != target->section()) ); int64_t srcAddr = atom->sectionOffset() + fit->offsetInAtom; int64_t dstAddr = target->sectionOffset() + addend; + if ( preload ) { + srcAddr = sAtomToAddress[atom] + fit->offsetInAtom; + dstAddr = sAtomToAddress[target] + addend; + } if ( target->section().type() == ld::Section::typeStub ) dstAddr = totalTextSize; int64_t displacement = dstAddr - srcAddr; TargetAndOffset finalTargetAndOffset = { target, addend }; const int64_t kBranchLimit = kBetweenRegions; - if ( displacement > kBranchLimit ) { + if ( crossSectionBranch && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) { + const ld::Atom* island; + AtomToIsland* region = regionsMap[0]; + AtomToIsland::iterator pos = region->find(finalTargetAndOffset); + if ( pos == region->end() ) { + island = makeBranchIsland(opts, fit->kind, 0, target, finalTargetAndOffset, atom->section(), true); + (*region)[finalTargetAndOffset] = island; + if (_s_log) fprintf(stderr, "added absolute branching island %p %s, displacement=%lld\n", + island, island->name(), displacement); + ++islandCount; + regionsIslands[0]->push_back(island); + } + else { + island = pos->second; + } + if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", island, island->name(), target->name(), atom->name()); + fixupWithTarget->u.target = island; + fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; + } + else if ( displacement > kBranchLimit ) { // create forward branch chain const ld::Atom* nextTarget = target; + if (_s_log) fprintf(stderr, "need forward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", + srcAddr, dstAddr, target->name()); for (int i=kIslandRegionsCount-1; i >=0 ; --i) { AtomToIsland* region = regionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( (srcAddr < islandRegionAddr) && (islandRegionAddr <= dstAddr) ) { + int64_t islandRegionAddr = regionAddresses[i]; + if ( (srcAddr < islandRegionAddr) && ((islandRegionAddr <= dstAddr)) ) { AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset); + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, nextTarget, finalTargetAndOffset, atom->section(), false); (*region)[finalTargetAndOffset] = island; - if (_s_log) fprintf(stderr, "added island %s to region %d for %s\n", island->name(), i, atom->name()); + if (_s_log) fprintf(stderr, "added forward branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); ++islandCount; nextTarget = island; @@ -521,7 +542,7 @@ void doPass(const Options& opts, ld::Internal& state) } } } - if (_s_log) fprintf(stderr, "using island %s for branch to %s from %s\n", nextTarget->name(), target->name(), atom->name()); + if (_s_log) fprintf(stderr, "using island %p %s for branch to %s from %s\n", nextTarget, nextTarget->name(), target->name(), atom->name()); fixupWithTarget->u.target = nextTarget; fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; } @@ -530,13 +551,14 @@ void doPass(const Options& opts, ld::Internal& state) const ld::Atom* prevTarget = target; for (int i=0; i < kIslandRegionsCount ; ++i) { AtomToIsland* region = regionsMap[i]; - int64_t islandRegionAddr = kBetweenRegions * (i+1); - if ( (dstAddr <= islandRegionAddr) && (islandRegionAddr < srcAddr) ) { + int64_t islandRegionAddr = regionAddresses[i]; + if ( (dstAddr < islandRegionAddr) && (islandRegionAddr <= srcAddr) ) { + if (_s_log) fprintf(stderr, "need backward branching island srcAdr=0x%08llX, dstAdr=0x%08llX, target=%s\n", srcAddr, dstAddr, target->name()); AtomToIsland::iterator pos = region->find(finalTargetAndOffset); if ( pos == region->end() ) { - ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset); + ld::Atom* island = makeBranchIsland(opts, fit->kind, i, prevTarget, finalTargetAndOffset, atom->section(), false); (*region)[finalTargetAndOffset] = island; - if (_s_log) fprintf(stderr, "added back island %s to region %d for %s\n", island->name(), i, atom->name()); + if (_s_log) fprintf(stderr, "added back branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); ++islandCount; prevTarget = island; @@ -546,7 +568,7 @@ void doPass(const Options& opts, ld::Internal& state) } } } - if (_s_log) fprintf(stderr, "using back island %s for %s\n", prevTarget->name(), atom->name()); + if (_s_log) fprintf(stderr, "using back island %p %s for %s\n", prevTarget, prevTarget->name(), atom->name()); fixupWithTarget->u.target = prevTarget; fixupWithTarget->binding = ld::Fixup::bindingDirectlyBound; } @@ -561,24 +583,15 @@ void doPass(const Options& opts, ld::Internal& state) std::vector newAtomList; newAtomList.reserve(textSection->atoms.size()+islandCount); - uint64_t regionIndex = 0; + int regionIndex = 0; for (std::vector::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ait++) { - newAtomList.push_back(*ait); - // copy over atoms until we find an island insertion point - // Note that the last insertion point is the last atom, so this loop never moves the iterator to atoms.end(). - while (*ait != branchIslandInsertionPoints[regionIndex]) { - ait++; - newAtomList.push_back(*ait); + const ld::Atom* atom = *ait; + newAtomList.push_back(atom); + if ( (regionIndex < kIslandRegionsCount) && (atom == branchIslandInsertionPoints[regionIndex]) ) { + std::vector* islands = regionsIslands[regionIndex]; + newAtomList.insert(newAtomList.end(), islands->begin(), islands->end()); + ++regionIndex; } - - // insert the branch island atoms after the insertion point atom - std::vector* regionIslands = regionsIslands[regionIndex]; - for (std::vector::iterator rit=regionIslands->begin(); rit != regionIslands->end(); rit++) { - const ld::Atom* islandAtom = *rit; - newAtomList.push_back(islandAtom); - if ( _s_log ) fprintf(stderr, "inserting island %s into __text section\n", islandAtom->name()); - } - regionIndex++; } // swap in new list of atoms for __text section textSection->atoms.clear(); @@ -588,6 +601,77 @@ void doPass(const Options& opts, ld::Internal& state) } +static void buildAddressMap(const Options& opts, ld::Internal& state) { + // Assign addresses to sections + state.setSectionSizesAndAlignments(); + state.assignFileOffsets(); + + // Assign addresses to atoms in a side table + const bool log = false; + if ( log ) fprintf(stderr, "buildAddressMap()\n"); + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + uint16_t maxAlignment = 0; + uint64_t offset = 0; + if ( log ) fprintf(stderr, " section=%s/%s, address=0x%08llX\n", sect->segmentName(), sect->sectionName(), sect->address); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; + uint32_t atomModulus = atom->alignment().modulus; + if ( atomAlignmentPowerOf2 > maxAlignment ) + maxAlignment = atomAlignmentPowerOf2; + // calculate section offset for this atom + uint64_t alignment = 1 << atomAlignmentPowerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t requiredModulus = atomModulus; + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += requiredModulus+alignment-currentModulus; + } + + if ( log ) fprintf(stderr, " 0x%08llX atom=%p, name=%s\n", sect->address+offset, atom, atom->name()); + sAtomToAddress[atom] = sect->address + offset; + + offset += atom->size(); + } + } + + +} + +void doPass(const Options& opts, ld::Internal& state) +{ + // only make branch islands in final linked images + if ( opts.outputKind() == Options::kObjectFile ) + return; + + // Allow user to disable branch island generation + if ( !opts.allowBranchIslands() ) + return; + + // only ARM needs branch islands + switch ( opts.architecture() ) { + case CPU_TYPE_ARM: + break; + default: + return; + } + + if ( opts.outputKind() == Options::kPreload ) { + buildAddressMap(opts, state); + } + + // scan sections and add island to each code section + for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->type() == ld::Section::typeCode ) + makeIslandsForSection(opts, state, sect); + } +} + + } // namespace branch_island } // namespace passes } // namespace ld diff --git a/ld64/src/ld/passes/compact_unwind.cpp b/ld64/src/ld/passes/compact_unwind.cpp index f86aee0..ad8a504 100644 --- a/ld64/src/ld/passes/compact_unwind.cpp +++ b/ld64/src/ld/passes/compact_unwind.cpp @@ -156,11 +156,10 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 std::vector lsdaIndex; makeLsdaIndex(uniqueEntries, lsdaIndex, lsdaIndexOffsetMap); - // calculate worst case size for all unwind info pages when allocating buffer const unsigned int entriesPerRegularPage = (4096-sizeof(unwind_info_regular_second_level_page_header))/sizeof(unwind_info_regular_second_level_entry); assert(uniqueEntries.size() > 0); - const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 1; + const unsigned int pageCount = ((uniqueEntries.size() - 1)/entriesPerRegularPage) + 2; _pagesForDelete = (uint8_t*)calloc(pageCount,4096); if ( _pagesForDelete == NULL ) { warning("could not allocate space for compact unwind info"); @@ -187,7 +186,11 @@ UnwindInfoAtom::UnwindInfoAtom(const std::vector& entries, uint6 secondLevelPagesStarts[secondLevelPageCount] = pageEnd; secondLevelFirstFuncs[secondLevelPageCount] = uniqueEntries[endIndex].func; ++secondLevelPageCount; - pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size + // if this requires more than one page, align so that next starts on page boundary + if ( (pageSize != 4096) && (endIndex > 0) ) { + pageEnd = (uint8_t*)((uintptr_t)(pageEnd) & -4096); + pageSize = 4096; // last page can be odd size, make rest up to 4096 bytes in size + } } _pages = pageEnd; _pagesSize = &_pagesForDelete[pageCount*4096] - pageEnd; @@ -294,6 +297,12 @@ bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc return ((enc & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF); } +template <> +bool UnwindInfoAtom::encodingMeansUseDwarf(compact_unwind_encoding_t enc) +{ + return ((enc & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF); +} + template void UnwindInfoAtom::compressDuplicates(const std::vector& entries, std::vector& uniqueEntries) { @@ -401,6 +410,14 @@ void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, co } template <> +void UnwindInfoAtom::addCompressedAddressOffsetFixup(uint32_t offset, const ld::Atom* func, const ld::Atom* fromFunc) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindSubtractTargetAddress, fromFunc)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) { _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); @@ -414,6 +431,12 @@ void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const l _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); } +template <> +void UnwindInfoAtom::addCompressedEncodingFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} template <> void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) @@ -430,6 +453,13 @@ void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::A } template <> +void UnwindInfoAtom::addRegularAddressFixup(uint32_t offset, const ld::Atom* func) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, func)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) { _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); @@ -444,6 +474,13 @@ void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld: } template <> +void UnwindInfoAtom::addRegularFDEOffsetFixup(uint32_t offset, const ld::Atom* fde) +{ + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k1of2, ld::Fixup::kindSetTargetSectionOffset, fde)); + _fixups.push_back(ld::Fixup(offset+4, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndianLow24of32)); +} + +template <> void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) { _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); @@ -458,6 +495,13 @@ void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom } template <> +void UnwindInfoAtom::addImageOffsetFixup(uint32_t offset, const ld::Atom* targ) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of2, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32)); +} + +template <> void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) { _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); @@ -473,6 +517,13 @@ void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, cons _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); } +template <> +void UnwindInfoAtom::addImageOffsetFixupPlusAddend(uint32_t offset, const ld::Atom* targ, uint32_t addend) +{ + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of3, ld::Fixup::kindSetTargetImageOffset, targ)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k2of3, ld::Fixup::kindAddAddend, addend)); + _fixups.push_back(ld::Fixup(offset, ld::Fixup::k3of3, ld::Fixup::kindStoreLittleEndian32)); +} @@ -552,6 +603,7 @@ unsigned int UnwindInfoAtom::makeCompressedSecondLevelPage(const std::vector< encodingIndex = commonEncodings.size() + pageSpecificEncodings.size(); if ( encodingIndex <= 255 ) { pageSpecificEncodings[encoding] = encodingIndex; + if (_s_log) fprintf(stderr, "makeCompressedSecondLevelPage(): pageSpecificEncodings[%d]=0x%08X\n", encodingIndex, encoding); } else { canDo = false; // case 3) @@ -685,7 +737,7 @@ static void getAllUnwindInfos(const ld::Internal& state, std::vectorbeginUnwind() == atom->endUnwind() ) { // be sure to mark that we have no unwind info for stuff in the TEXT segment without unwind info - if ( atom->section().type() == ld::Section::typeCode ) { + if ( (atom->section().type() == ld::Section::typeCode) && (atom->size() !=0) ) { entries.push_back(UnwindEntry(atom, address, 0, NULL, NULL, NULL, 0)); } } @@ -782,6 +834,11 @@ static void makeFinalLinkedImageCompactUnwindSection(const Options& opts, ld::In state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new UnwindInfoAtom(entries, ehFrameSize)); + break; +#endif default: assert(0 && "no compact unwind for arch"); } @@ -830,13 +887,17 @@ template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup:: template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian32; template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64; template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; +#if SUPPORT_ARCH_arm64 +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64; +template <> ld::Fixup::Kind CompactUnwindAtom::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64; +#endif template CompactUnwindAtom::CompactUnwindAtom(ld::Internal& state,const ld::Atom* funcAtom, uint32_t startOffset, uint32_t len, uint32_t cui) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(log2(sizeof(pint_t)))), _atom(funcAtom), _startOffset(startOffset), _len(len), _compactUnwindInfo(cui) { _fixups.push_back(ld::Fixup(macho_compact_unwind_entry

::codeStartFieldOffset(), ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, funcAtom)); @@ -886,6 +947,11 @@ static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, cons state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new CompactUnwindAtom(state, atom, startOffset, endOffset-startOffset, cui)); + break; +#endif } } diff --git a/ld64/src/ld/passes/dtrace_dof.cpp b/ld64/src/ld/passes/dtrace_dof.cpp index 74328ff..6f8a544 100644 --- a/ld64/src/ld/passes/dtrace_dof.cpp +++ b/ld64/src/ld/passes/dtrace_dof.cpp @@ -34,6 +34,7 @@ #include #include "ld.hpp" +#include "MachOFileAbstraction.hpp" #include "dtrace_dof.h" // prototype for entry point in libdtrace.dylib @@ -122,6 +123,10 @@ void doPass(const Options& opts, ld::Internal& internal) // only make __dof section in final linked images if ( opts.outputKind() == Options::kObjectFile ) return; + + // skip making __dof section if command line option said not to + if ( ! opts.generateDtraceDOF() ) + return; // scan all atoms looking for dtrace probes std::vector probeSites; @@ -138,11 +143,13 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::kindStoreX86DtraceCallSiteNop: case ld::Fixup::kindStoreARMDtraceCallSiteNop: case ld::Fixup::kindStoreThumbDtraceCallSiteNop: + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); break; case ld::Fixup::kindDtraceExtra: @@ -164,6 +171,7 @@ void doPass(const Options& opts, ld::Internal& internal) case CPU_TYPE_I386: case CPU_TYPE_X86_64: case CPU_TYPE_ARM: + case CPU_TYPE_ARM64: storeKind = ld::Fixup::kindStoreLittleEndian32; break; default: diff --git a/ld64/src/ld/passes/got.cpp b/ld64/src/ld/passes/got.cpp index 3e6824f..2b9da3a 100644 --- a/ld64/src/ld/passes/got.cpp +++ b/ld64/src/ld/passes/got.cpp @@ -31,8 +31,10 @@ #include #include +#include "MachOFileAbstraction.hpp" #include "ld.hpp" #include "got.h" +#include "configure.h" namespace ld { namespace passes { @@ -42,17 +44,18 @@ class File; // forward reference class GOTEntryAtom : public ld::Atom { public: - GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport) + GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool is64) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, - symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), - _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), - _target(target) + symbolTableNotIn, false, false, false, (is64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2))), + _fixup(0, ld::Fixup::k1of1, (is64 ? ld::Fixup::kindStoreTargetAddressLittleEndian64 : ld::Fixup::kindStoreTargetAddressLittleEndian32), target), + _target(target), + _is64(is64) { _fixup.weakImport = weakImport; internal.addAtom(*this); } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _target->name(); } - virtual uint64_t size() const { return 8; } + virtual uint64_t size() const { return (_is64 ? 8 : 4); } virtual uint64_t objectAddress() const { return 0; } virtual void copyRawContent(uint8_t buffer[]) const { } virtual void setScope(Scope) { } @@ -62,6 +65,7 @@ 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/ld64/src/ld/passes/objc.cpp b/ld64/src/ld/passes/objc.cpp index d471d98..d921a64 100644 --- a/ld64/src/ld/passes/objc.cpp +++ b/ld64/src/ld/passes/objc.cpp @@ -54,6 +54,7 @@ struct objc_image_info { #define OBJC_IMAGE_REQUIRES_GC (1<<2) #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) #define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) +#define OBJC_IMAGE_IS_SIMULATED (1<<5) @@ -111,6 +112,9 @@ ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, if ( compaction ) value |= OBJC_IMAGE_SUPPORTS_COMPACTION; break; + case ld::File::objcConstraintRetainReleaseForSimulator: + value |= OBJC_IMAGE_IS_SIMULATED; + break; } _content.version = 0; @@ -1176,10 +1180,18 @@ void doPass(const Options& opts, ld::Internal& state) opts.objCABIVersion2POverride() ? true : false)); break; #endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, true)); break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + true)); + break; +#endif default: assert(0 && "unknown objc arch"); } @@ -1195,17 +1207,20 @@ void doPass(const Options& opts, ld::Internal& state) #endif #if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - // disable optimization until fully tested if ( opts.objCABIVersion2POverride() ) OptimizeCategories::doit(opts, state); break; #endif #if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - // disable optimization until fully tested OptimizeCategories::doit(opts, state); break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + // disabled until tested + break; +#endif default: assert(0 && "unknown objc arch"); } diff --git a/ld64/src/ld/passes/order.cpp b/ld64/src/ld/passes/order.cpp index b4be79f..684cb79 100644 --- a/ld64/src/ld/passes/order.cpp +++ b/ld64/src/ld/passes/order.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include "ld.hpp" @@ -122,7 +123,7 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) { if ( left == right ) return false; - + // magic section$start symbol always sorts to the start of its section if ( left->contentType() == ld::Atom::typeSectionStart ) return true; @@ -162,6 +163,32 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) if ( right->contentType() == ld::Atom::typeSectionEnd ) return true; + // aliases sort before their target + bool leftIsAlias = left->isAlias(); + if ( leftIsAlias ) { + for (ld::Fixup::iterator fit=left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target == right ) + return true; // left already before right + left = fit->u.target; // sort as if alias was its target + break; + } + } + } + bool rightIsAlias = right->isAlias(); + if ( rightIsAlias ) { + for (ld::Fixup::iterator fit=right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) { + if ( fit->kind == ld::Fixup::kindNoneFollowOn ) { + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + if ( fit->u.target == left ) + return false; // need to swap, alias is after target + right = fit->u.target; // continue with sort as if right was target + break; + } + } + } + // the __common section can have real or tentative definitions // we want the real ones to sort before tentative ones bool leftIsTent = (left->definition() == ld::Atom::definitionTentative); @@ -204,8 +231,6 @@ bool Layout::Comparer::operator()(const ld::Atom* left, const ld::Atom* right) int64_t addrDiff = left->objectAddress() - right->objectAddress(); if ( addrDiff == 0 ) { // have same address so one might be an alias, and aliases need to sort before target - bool leftIsAlias = left->isAlias(); - bool rightIsAlias = right->isAlias(); if ( leftIsAlias != rightIsAlias ) return leftIsAlias; @@ -506,22 +531,32 @@ void Layout::buildOrdinalOverrideMap() warning("only %u out of %lu order_file symbols were applicable", matchCount, _options.orderedSymbolsCount() ); } - // When order file used on data, turn ordered zero fill symbols into zeroed data if ( ! moveToData.empty() ) { + // only move zero fill symbols to __data if there is a __data section + ld::Internal::FinalSection* dataSect = NULL; for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - switch ( sect->type() ) { - case ld::Section::typeZeroFill: - case ld::Section::typeTentativeDefs: - sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end()); - break; - case ld::Section::typeUnclassified: - if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) - sect->atoms.insert(sect->atoms.end(), moveToData.begin(), moveToData.end()); - break; - default: - break; + if ( sect->type() == ld::Section::typeUnclassified ) { + if ( (strcmp(sect->sectionName(), "__data") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + dataSect = sect; + } + } + + if ( dataSect != NULL ) { + // add atoms to __data + dataSect->atoms.insert(dataSect->atoms.end(), moveToData.begin(), moveToData.end()); + // remove atoms from original sections + for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + switch ( sect->type() ) { + case ld::Section::typeZeroFill: + case ld::Section::typeTentativeDefs: + sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), InSet(moveToData)), sect->atoms.end()); + break; + default: + break; + } } } } @@ -539,6 +574,7 @@ void Layout::doPass() // sort atoms in each section for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; + //fprintf(stderr, "sorting section %s\n", sect->sectionName()); std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); } @@ -547,7 +583,7 @@ void Layout::doPass() // ld::Internal::FinalSection* sect = *sit; // for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { // const ld::Atom* atom = *ait; - // fprintf(stderr, "\t%s\t%s\n", sect->sectionName(), atom->name()); + // fprintf(stderr, "\t%p\t%s\t%s\n", atom, sect->sectionName(), atom->name()); // } //} diff --git a/ld64/src/ld/passes/stubs/stub_arm64.hpp b/ld64/src/ld/passes/stubs/stub_arm64.hpp new file mode 100644 index 0000000..63382e5 --- /dev/null +++ b/ld64/src/ld/passes/stubs/stub_arm64.hpp @@ -0,0 +1,396 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// already in ld::passes::stubs namespace +namespace arm64 { + + + +class FastBindingPointerAtom : public ld::Atom { +public: + FastBindingPointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + pass.internal()->compressedFastBinderProxy) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return "fast binder pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup)[1]; } + +private: + mutable ld::Fixup _fixup; + + static ld::Section _s_section; +}; + +ld::Section FastBindingPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +class ImageCachePointerAtom : public ld::Atom { +public: + ImageCachePointerAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)) { pass.addAtom(*this); } + + virtual const ld::File* file() const { return NULL; } + virtual const char* name() const { return "image cache pointer"; } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +private: + + static ld::Section _s_section; +}; + +ld::Section ImageCachePointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + + + + +// +// The stub-helper-helper is the common code factored out of each helper function. +// It is in the same section as the stub-helpers. +// Similar to the PLT0 entry in ELF. +// +class StubHelperHelperAtom : public ld::Atom { +public: + StubHelperHelperAtom(ld::passes::stubs::Pass& pass) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedImageCache(pass)), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedImageCache(pass)), + _fixup3(12, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, compressedFastBinder(pass)), + _fixup4(16, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, compressedFastBinder(pass)), + _fixup5(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_ADD, 0, 4), + _fixup6(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 12, 16) + { pass.addAtom(*this); } + + virtual ld::File* file() const { return NULL; } + virtual const char* name() const { return "helper helper"; } + virtual uint64_t size() const { return 24; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0x90000011); // ADRP X17, dyld_mageLoaderCache@page + OSWriteLittleInt32(&buffer[ 4], 0, 0x91000231); // ADD X17, X17, dyld_mageLoaderCache@pageoff + OSWriteLittleInt32(&buffer[ 8], 0, 0xA9BF47F0); // STP X16/X17, [SP, #-16]! + OSWriteLittleInt32(&buffer[12], 0, 0x90000010); // ADRP X16, _fast_lazy_bind@page + OSWriteLittleInt32(&buffer[16], 0, 0xF9400210); // LDR X16, [X16,_fast_lazy_bind@pageoff] + OSWriteLittleInt32(&buffer[20], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup6)[1]; } + +private: + static ld::Atom* compressedImageCache(ld::passes::stubs::Pass& pass) { + if ( pass.compressedImageCache == NULL ) + pass.compressedImageCache = new ImageCachePointerAtom(pass); + return pass.compressedImageCache; + } + static ld::Atom* compressedFastBinder(ld::passes::stubs::Pass& pass) { + if ( pass.compressedFastBinderPointer == NULL ) + pass.compressedFastBinderPointer = new FastBindingPointerAtom(pass); + return pass.compressedFastBinderPointer; + } + + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + ld::Fixup _fixup4; + ld::Fixup _fixup5; + ld::Fixup _fixup6; + + static ld::Section _s_section; +}; + +ld::Section StubHelperHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class StubHelperAtom : public ld::Atom { +public: + StubHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, helperHelper(pass)), + _fixup2(8, ld::Fixup::k1of2, ld::Fixup::kindSetLazyOffset, lazyPointer), + _fixup3(8, ld::Fixup::k2of2, ld::Fixup::kindStoreLittleEndian32) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x18000050); // LDR W16, L0 + OSWriteLittleInt32(&buffer[4], 0, 0x14000000); // B helperhelper + OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // L0: .long 0 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + static ld::Atom* helperHelper(ld::passes::stubs::Pass& pass) { + if ( pass.compressedHelperHelper == NULL ) + pass.compressedHelperHelper = new StubHelperHelperAtom(pass); + return pass.compressedHelperHelper; + } + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + +class ResolverHelperAtom : public ld::Atom { +public: + ResolverHelperAtom(ld::passes::stubs::Pass& pass, const ld::Atom* lazyPointer, + const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStubHelper, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _fixup1(24, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Branch26, &stubTo), + _fixup2(28, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, lazyPointer), + _fixup3(32, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, lazyPointer) { } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 68; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[ 0], 0, 0xa9bf7bfd); // stp fp, lr, [sp, #-16]! + OSWriteLittleInt32(&buffer[ 4], 0, 0x910003fd); // mov fp, sp + OSWriteLittleInt32(&buffer[ 8], 0, 0xa9bf03e1); // stp x1, x0, [sp, #-16]! + OSWriteLittleInt32(&buffer[12], 0, 0xa9bf0be3); // stp x3, x2, [sp, #-16]! + OSWriteLittleInt32(&buffer[16], 0, 0xa9bf13e5); // stp x5, x4, [sp, #-16]! + OSWriteLittleInt32(&buffer[20], 0, 0xa9bf1be7); // stp x7, x6, [sp, #-16]! + OSWriteLittleInt32(&buffer[24], 0, 0x94000000); // bl _foo + OSWriteLittleInt32(&buffer[28], 0, 0x90000010); // adrp x16, lazy_pointer@PAGE + OSWriteLittleInt32(&buffer[32], 0, 0x91000210); // add x16, x16, lazy_pointer@PAGEOFF + OSWriteLittleInt32(&buffer[36], 0, 0xf9000200); // str x0, [x16] + OSWriteLittleInt32(&buffer[40], 0, 0xaa0003f0); // mov x16, x0 + OSWriteLittleInt32(&buffer[44], 0, 0xa8c11be7); // ldp x7, x6, [sp], #16 + OSWriteLittleInt32(&buffer[48], 0, 0xa8c113e5); // ldp x5, x4, [sp], #16 + OSWriteLittleInt32(&buffer[52], 0, 0xa8c10be3); // ldp x3, x2, [sp], #16 + OSWriteLittleInt32(&buffer[56], 0, 0xa8c103e1); // ldp x1, x0, [sp], #16 + OSWriteLittleInt32(&buffer[60], 0, 0xa8c17bfd); // ldp fp, lr, [sp], #16 + OSWriteLittleInt32(&buffer[64], 0, 0xd61f0200); // br x16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + + const ld::Atom& _stubTo; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::Section::typeStubHelper); + + + +class LazyPointerAtom : public ld::Atom { +public: + LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _helper(pass, this, stubTo), + _resolverHelper(pass, this, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, + stubToResolver ? &_resolverHelper : + (stubToGlobalWeakDef ? &stubTo : &_helper)), + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2.weakImport = weakImport; pass.addAtom(*this); + if ( stubToResolver ) + pass.addAtom(_resolverHelper); + else if ( !stubToGlobalWeakDef ) + pass.addAtom(_helper); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + StubHelperAtom _helper; + ResolverHelperAtom _resolverHelper; + mutable ld::Fixup _fixup1; + ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); + + +class StubAtom : public ld::Atom { +public: + StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer), + _fixup3(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 0, 4) + { pass.addAtom(*this); } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x90000010); // ADRP X16, lazy_pointer@page + OSWriteLittleInt32(&buffer[4], 0, 0xF9400210); // LDR X16, [X16, lazy_pointer@pageoff] + OSWriteLittleInt32(&buffer[8], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup3)[1]; } + +private: + const ld::Atom& _stubTo; + LazyPointerAtom _lazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + mutable ld::Fixup _fixup3; + + static ld::Section _s_section; +}; + +ld::Section StubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeStub); + + + +class NonLazyPointerAtom : public ld::Atom { +public: + NonLazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, + ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, + symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), + _stubTo(stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, &stubTo) { + pass.addAtom(*this); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _stubTo.name(); } + virtual uint64_t size() const { return 8; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; } + +private: + const ld::Atom& _stubTo; + ld::Fixup _fixup1; + + static ld::Section _s_section; +}; + +ld::Section NonLazyPointerAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); + + +class KextStubAtom : public ld::Atom { +public: + KextStubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo) + : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, + symbolTableIn, false, false, false, ld::Atom::Alignment(1)), + _stubTo(stubTo), + _nonLazyPointer(pass, stubTo), + _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_nonLazyPointer), + _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_nonLazyPointer) { + pass.addAtom(*this); + asprintf((char**)&_name, "%s.stub", _stubTo.name()); + } + + virtual const ld::File* file() const { return _stubTo.file(); } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 12; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { + OSWriteLittleInt32(&buffer[0], 0, 0x90000010); // ADRP X16, non_lazy_pointer@page + OSWriteLittleInt32(&buffer[4], 0, 0xF9400210); // LDR X16, [X16, non_lazy_pointer@pageoff] + OSWriteLittleInt32(&buffer[8], 0, 0xD61F0200); // BR X16 + } + virtual void setScope(Scope) { } + virtual ld::Fixup::iterator fixupsBegin() const { return &_fixup1; } + virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } + +private: + const ld::Atom& _stubTo; + const char* _name; + NonLazyPointerAtom _nonLazyPointer; + mutable ld::Fixup _fixup1; + mutable ld::Fixup _fixup2; + + static ld::Section _s_section; +}; + +ld::Section KextStubAtom::_s_section("__TEXT", "__stubs", ld::Section::typeCode); + + +} // namespace x86_64 + diff --git a/ld64/src/ld/passes/stubs/stubs.cpp b/ld64/src/ld/passes/stubs/stubs.cpp index 5ea1b05..c01f3f9 100644 --- a/ld64/src/ld/passes/stubs/stubs.cpp +++ b/ld64/src/ld/passes/stubs/stubs.cpp @@ -35,8 +35,8 @@ #include #include "Options.h" -#include "ld.hpp" #include "MachOFileAbstraction.hpp" +#include "ld.hpp" #include "make_stubs.h" @@ -90,8 +90,9 @@ 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 ) @@ -217,6 +221,14 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) } break; #endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) + return new ld::passes::stubs::arm64::KextStubAtom(*this, target); + else + return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + break; +#endif } throw "unsupported arch for stub"; } diff --git a/ld64/src/ld/passes/tlvp.cpp b/ld64/src/ld/passes/tlvp.cpp index 6a58fdf..e84fc25 100644 --- a/ld64/src/ld/passes/tlvp.cpp +++ b/ld64/src/ld/passes/tlvp.cpp @@ -142,6 +142,10 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad: case ld::Fixup::kindStoreX86PCRel32TLVLoad: case ld::Fixup::kindStoreX86Abs32TLVLoad: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: +#endif ref.fixupWithTLVStore = fit; break; default: @@ -230,6 +234,14 @@ void doPass(const Options& opts, ld::Internal& internal) case ld::Fixup::kindStoreX86Abs32TLVLoad: it->fixupWithTLVStore->kind = ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA; break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21; + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12; + break; +#endif default: assert(0 && "bad store kind for TLV optimization"); } diff --git a/ld64/src/other/ObjectDump.cpp b/ld64/src/other/ObjectDump.cpp index cc673a3..c9eb46c 100644 --- a/ld64/src/other/ObjectDump.cpp +++ b/ld64/src/other/ObjectDump.cpp @@ -804,6 +804,39 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbHigh16: printf(", then store high-16 in Thumb movt"); break; + case ld::Fixup::kindStoreARM64Branch26: + printf(", then store as ARM64 26-bit pcrel branch"); + break; + case ld::Fixup::kindStoreARM64Page21: + printf(", then store as ARM64 21-bit pcrel ADRP"); + break; + case ld::Fixup::kindStoreARM64PageOff12: + printf(", then store as ARM64 12-bit offset"); + break; + case ld::Fixup::kindStoreARM64GOTLoadPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of GOT"); + break; + case ld::Fixup::kindStoreARM64GOTLoadPageOff12: + printf(", then store as ARM64 12-bit page offset of GOT"); + break; + case ld::Fixup::kindStoreARM64GOTLeaPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of GOT lea"); + break; + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + printf(", then store as ARM64 12-bit page offset of GOT lea"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + printf(", then store as ARM64 21-bit pcrel ADRP of TLVP"); + break; + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + printf(", then store as ARM64 12-bit page offset of TLVP"); + break; + case ld::Fixup::kindStoreARM64PointerToGOT: + printf(", then store as 64-bit pointer to GOT entry"); + break; + case ld::Fixup::kindStoreARM64PCRelToGOT: + printf(", then store as 32-bit delta to GOT entry"); + break; case ld::Fixup::kindDtraceExtra: printf("dtrace static probe extra info"); break; @@ -825,12 +858,21 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: printf("Thumb dtrace static is-enabled site"); break; + case ld::Fixup::kindStoreARM64DtraceCallSiteNop: + printf("ARM64 dtrace static probe site"); + break; + case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: + printf("ARM64 dtrace static is-enabled site"); + break; case ld::Fixup::kindLazyTarget: printf("lazy reference to external symbol %s", referenceTargetAtomName(ref)); break; case ld::Fixup::kindSetLazyOffset: printf("offset of lazy binding info for %s", referenceTargetAtomName(ref)); break; + case ld::Fixup::kindIslandTarget: + printf("ultimate target of island %s", referenceTargetAtomName(ref)); + break; case ld::Fixup::kindDataInCodeStartData: printf("start of data in code"); break; @@ -849,6 +891,46 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindDataInCodeEnd: printf("end of data in code"); break; + case ld::Fixup::kindLinkerOptimizationHint: +#if SUPPORT_ARCH_arm64 + ld::Fixup::LOH_arm64 extra; + extra.addend = ref->u.addend; + printf("ARM64 hint: "); + switch(extra.info.kind) { + case LOH_ARM64_ADRP_ADRP: + printf("ADRP-ADRP"); + break; + case LOH_ARM64_ADRP_LDR: + printf("ADRP-LDR"); + break; + case LOH_ARM64_ADRP_ADD_LDR: + printf("ADRP-ADD-LDR"); + break; + case LOH_ARM64_ADRP_LDR_GOT_LDR: + printf("ADRP-LDR-GOT-LDR"); + break; + case LOH_ARM64_ADRP_ADD_STR: + printf("ADRP-ADD-STR"); + break; + case LOH_ARM64_ADRP_LDR_GOT_STR: + printf("ADRP-LDR-GOT-STR"); + break; + case LOH_ARM64_ADRP_ADD: + printf("ADRP-ADD"); + break; + default: + printf("kind=%d", extra.info.kind); + break; + } + printf(", offset1=0x%X", (extra.info.delta1 << 2) + ref->offsetInAtom); + if ( extra.info.count > 0 ) + printf(", offset2=0x%X", (extra.info.delta2 << 2) + ref->offsetInAtom); + if ( extra.info.count > 1 ) + printf(", offset3=0x%X", (extra.info.delta3 << 2) + ref->offsetInAtom); + if ( extra.info.count > 2 ) + printf(", offset4=0x%X", (extra.info.delta4 << 2) + ref->offsetInAtom); +#endif + break; case ld::Fixup::kindStoreTargetAddressLittleEndian32: printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref)); break; @@ -898,6 +980,34 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32: case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: printf("tlv template offset of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + printf("ARM64 store 26-bit pcrel branch to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64Page21: + printf("ARM64 store 21-bit pcrel ADRP to %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + printf("ARM64 store 12-bit page offset of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPage21: + printf("ARM64 store 21-bit pcrel ADRP to TLV for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64TLVPageOff12: + printf("ARM64 store 12-bit page offset of TLV of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + printf("ARM64 store 21-bit pcrel ADRP to GOT for %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + printf("ARM64 store 12-bit page offset of GOT of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + printf("ARM64 store 21-bit pcrel ADRP for lea of %s", referenceTargetAtomName(ref)); + break; + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + printf("ARM64 store 12-bit page offset of lea of %s", referenceTargetAtomName(ref)); + break; //default: // printf("unknown fixup"); // break; @@ -1116,7 +1226,10 @@ static ld::relocatable::File* createReader(const char* path) objOpts.architecture = sPreferredArch; objOpts.objSubtypeMustMatch = false; objOpts.logAllFiles = false; - objOpts.convertUnwindInfo = true; + objOpts.warnUnwindConversionProblems = true; + objOpts.keepDwarfUnwind = false; + objOpts.forceDwarfConversion = false; + objOpts.verboseOptimizationHints = true; objOpts.subType = sPreferredSubArch; #if 1 if ( ! foundFatSlice ) { @@ -1133,7 +1246,7 @@ static ld::relocatable::File* createReader(const char* path) return objResult; // see if it is an llvm object file - objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false); + objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false, true); if ( objResult != NULL ) return objResult; diff --git a/ld64/src/other/dyldinfo.cpp b/ld64/src/other/dyldinfo.cpp index 71f13b8..6ac3311 100644 --- a/ld64/src/other/dyldinfo.cpp +++ b/ld64/src/other/dyldinfo.cpp @@ -252,6 +252,26 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) } #endif +#if SUPPORT_ARCH_arm64 +template <> +bool DyldInfoPrinter::validFile(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

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

* header = (const macho_header

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

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

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

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

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

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

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

* reloc) +{ + throw "local relocations not used for arm64"; +} +#endif + + template void MachOChecker::checkRelocations() { @@ -1603,6 +1664,11 @@ static void check(const char* path) MachOChecker::make(p, length, path); } #endif +#if SUPPORT_ARCH_arm64 + else if ( MachOChecker::validFile(p) ) { + MachOChecker::make(p, length, path); + } +#endif else { throw "not a known file type"; } diff --git a/ld64/src/other/unwinddump.cpp b/ld64/src/other/unwinddump.cpp index 731f2a3..3da01c5 100644 --- a/ld64/src/other/unwinddump.cpp +++ b/ld64/src/other/unwinddump.cpp @@ -35,7 +35,7 @@ #include #include - +#include "configure.h" #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -99,7 +99,9 @@ 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/ld64/unit-tests/include/common.makefile b/ld64/unit-tests/include/common.makefile index 41cf6cf..3f4647d 100644 --- a/ld64/unit-tests/include/common.makefile +++ b/ld64/unit-tests/include/common.makefile @@ -13,7 +13,7 @@ MYDIR=$(shell cd ../../bin;pwd) LD = ld OBJECTDUMP = ObjectDump MACHOCHECK = machocheck -OTOOL = otool +OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/otool REBASE = rebase DYLDINFO = dyldinfo @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path) +IOS_SDK = $(shell xcodebuild -sdk iphoneos7.0.internal -version Path 2>/dev/null) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) @@ -125,6 +125,21 @@ else FILEARCH = $(ARCH) endif +ifeq ($(ARCH),arm64) + LDFLAGS := -syslibroot $(IOS_SDK) + CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + #CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + #CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/clang-loh -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + VERSION_NEW_LINKEDIT = -miphoneos-version-min=7.0 + VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 + LD_SYSROOT = -syslibroot $(IOS_SDK) + LD_NEW_LINKEDIT = -ios_version_min 7.0 + OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/otool + #OTOOL = $(shell xcrun --sdk iphoneos.internal -find otool) +else + FILEARCH = $(ARCH) +endif RM = rm RMFLAGS = -rf diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/branch-islands/Makefile index 8e1870b..9d30491 100644 --- a/ld64/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/branch-islands/Makefile @@ -33,9 +33,9 @@ run: all all: # Verify that we fail if there is no valid place to insert branch islands. - ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} + #${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} - ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} + ${CC} ${CCFLAGS} hello.c space.s extra.c -Os -o hello ${ARCH_FLAGS} ${PASS_IFF_GOOD_MACHO} hello diff --git a/ld64/unit-tests/test-cases/branch-islands/extra.c b/ld64/unit-tests/test-cases/branch-islands/extra.c index a1991fe..3f43392 100644 --- a/ld64/unit-tests/test-cases/branch-islands/extra.c +++ b/ld64/unit-tests/test-cases/branch-islands/extra.c @@ -1,8 +1,10 @@ #include +extern void back(); void foo() { fprintf(stdout, "foo\n"); + back(); } diff --git a/ld64/unit-tests/test-cases/branch-islands/hello.c b/ld64/unit-tests/test-cases/branch-islands/hello.c index 3037663..0738f0e 100644 --- a/ld64/unit-tests/test-cases/branch-islands/hello.c +++ b/ld64/unit-tests/test-cases/branch-islands/hello.c @@ -5,7 +5,10 @@ extern void foo(); int main() { fprintf(stdout, "hello\n"); - foo(); + foo(); return 0; } +void back() +{ +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/branch-islands/space.s b/ld64/unit-tests/test-cases/branch-islands/space.s index 0218bea..a15a2c2 100644 --- a/ld64/unit-tests/test-cases/branch-islands/space.s +++ b/ld64/unit-tests/test-cases/branch-islands/space.s @@ -35,11 +35,11 @@ _prejunk: #if __thumb2__ // thumb2 branches are +/- 16MB _space1: - .space 14*1024*1024 + .space 13*1024*1024 _space2: - .space 14*1024*1024 + .space 13*1024*1024 _space3: - .space 14*1024*1024 + .space 13*1024*1024 #elif __thumb__ diff --git a/ld64/unit-tests/test-cases/branch-long/Makefile b/ld64/unit-tests/test-cases/branch-long/Makefile new file mode 100644 index 0000000..9b41703 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-long/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +# +# test thumb2 branch ranges +# + +run: all + +all: all-${ARCH} + +all-i386: + ${PASS_IFF} true + +all-x86_64: + ${PASS_IFF} true + +all-armv7: + ${CC} ${CCFLAGS} -static foo.c -c + ${CC} ${CCFLAGS} bar.s -c + # verify islands are created and used + ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar -nostdlib -Wl,-preload -segaddr __MY 0x3000000 + ${OTOOL} -s __MY __text -V foobar | grep '_myweak1.island:' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar | grep '_foo2.island:' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar | grep '_foo.island:' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar | grep "bl\t_foo.island" | ${FAIL_IF_EMPTY} + # verify that is close enough islands are bypassed and target called directly + ${CC} ${CCFLAGS} foo.o bar.o -e _foo -o foobar2 -nostdlib -Wl,-preload -segaddr __MY 0x30000 + ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_foo2' | ${FAIL_IF_EMPTY} + ${OTOOL} -s __MY __text -V foobar2 | grep 'bl\t_myweak1' | ${FAIL_IF_EMPTY} + ${PASS_IFF} true + +clean: + rm *.o foobar* diff --git a/ld64/unit-tests/test-cases/branch-long/bar.s b/ld64/unit-tests/test-cases/branch-long/bar.s new file mode 100644 index 0000000..528512a --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-long/bar.s @@ -0,0 +1,24 @@ + +#if __arm__ + + .section __MY,__text,regular,pure_instructions + .align 4 + +#if __thumb__ + .thumb_func _bar + .code 16 +#endif + .globl _bar +_bar: + nop + bl _foo + blx _foo2 + bl _myweak1 + + +#endif // __arm__ + + + + .subsections_via_symbols + \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/branch-long/foo.c b/ld64/unit-tests/test-cases/branch-long/foo.c new file mode 100644 index 0000000..60e96d2 --- /dev/null +++ b/ld64/unit-tests/test-cases/branch-long/foo.c @@ -0,0 +1,25 @@ + +int x = 1; +int y = 2; + +__attribute__((weak)) +void myweak1() +{ +} + +int foo() +{ + myweak1(); + return 1; +} + +int foo1() +{ + return x; +} + +int foo2() +{ + return y; +} + diff --git a/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile b/ld64/unit-tests/test-cases/coalesce-force/Makefile similarity index 61% copy from ld64/unit-tests/test-cases/eh-coalescing-r/Makefile copy to ld64/unit-tests/test-cases/coalesce-force/Makefile index 3fb8a25..92aadd7 100644 --- a/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile +++ b/ld64/unit-tests/test-cases/coalesce-force/Makefile @@ -1,15 +1,15 @@ ## -# Copyright (c) 2008 Apple Inc. All rights reserved. +# 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, @@ -17,31 +17,29 @@ # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. -# +# # @APPLE_LICENSE_HEADER_END@ ## TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - -SHELL = bash # use bash shell so we can redirect just stderr - - -# -# -# comdat warnings in ld -r # -# also use -falign-functions to force an out of order coalesing +# Test -force_symbols_coalesce_list # + + run: all -all: - ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o - ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -falign-functions=32 - ${CXX} ${CCXXFLAGS} baz.cxx -c -o baz.o - ${LD} -r foo.o bar.o baz.o -o foobarbaz.o 2> warnings.log - grep warning warnings.log | ${PASS_IFF_EMPTY} +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 foo.o bar.o baz.o foobarbaz.o warnings.log + rm -rf libfoo.dylib libfoo2.dylib diff --git a/ld64/unit-tests/test-cases/coalesce-force/foo.c b/ld64/unit-tests/test-cases/coalesce-force/foo.c new file mode 100644 index 0000000..411d4c0 --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce-force/foo.c @@ -0,0 +1,21 @@ + + +void foo1() {} +void foo3() {} + + +__attribute__((weak)) void foo2() {} +__attribute__((weak)) void foo4() {} + + +void wildcheck() {} +void willnot() {} + + + +__attribute__((weak)) void patterncheck() {} +__attribute__((weak)) void patnot() {} + + +void* pointers[] = { &foo1, &foo2, &foo3, &foo4, &wildcheck, &willnot, &patterncheck, &patnot }; + diff --git a/ld64/unit-tests/test-cases/coalesce-force/foo.exp b/ld64/unit-tests/test-cases/coalesce-force/foo.exp new file mode 100644 index 0000000..7268896 --- /dev/null +++ b/ld64/unit-tests/test-cases/coalesce-force/foo.exp @@ -0,0 +1,2 @@ +_foo1 +_wild* diff --git a/ld64/unit-tests/test-cases/cpu-sub-types/Makefile b/ld64/unit-tests/test-cases/cpu-sub-types/Makefile index f773c11..78a2484 100644 --- a/ld64/unit-tests/test-cases/cpu-sub-types/Makefile +++ b/ld64/unit-tests/test-cases/cpu-sub-types/Makefile @@ -27,6 +27,8 @@ include ${TESTROOT}/include/common.makefile # Validate cpu subtypes processing # +CC_ARM = $(shell xcrun -find clang) -miphoneos-version-min=5.0 -isysroot ${IOS_SDK} + test: test-${ARCH} test-ppc64: @@ -42,31 +44,31 @@ test-armv6: test-arm test-armv7: test-arm test-arm: - clang foo.c -arch armv4t -c -o foo-v4.o + ${CC_ARM} foo.c -arch armv4t -c -o foo-v4.o ${FAIL_IF_BAD_OBJ} foo-v4.o - clang foo.c -arch armv5 -c -o foo-v5.o + ${CC_ARM} foo.c -arch armv5 -c -o foo-v5.o ${FAIL_IF_BAD_OBJ} foo-v5.o - clang foo.c -arch armv6 -c -o foo-v6.o + ${CC_ARM} foo.c -arch armv6 -c -o foo-v6.o ${FAIL_IF_BAD_OBJ} foo-v6.o - clang foo.c -arch armv7 -c -o foo-v7.o + ${CC_ARM} foo.c -arch armv7 -c -o foo-v7.o ${FAIL_IF_BAD_OBJ} foo-v7.o - clang foo.c -arch xscale -c -o foo-xscale.o + ${CC_ARM} foo.c -arch xscale -c -o foo-xscale.o ${FAIL_IF_BAD_OBJ} foo-xscale.o - clang main.c -arch armv4t -c -o main-v4.o + ${CC_ARM} main.c -arch armv4t -c -o main-v4.o ${FAIL_IF_BAD_OBJ} main-v4.o - clang main.c -arch armv5 -c -o main-v5.o + ${CC_ARM} main.c -arch armv5 -c -o main-v5.o ${FAIL_IF_BAD_OBJ} main-v5.o - clang main.c -arch armv6 -c -o main-v6.o + ${CC_ARM} main.c -arch armv6 -c -o main-v6.o ${FAIL_IF_BAD_OBJ} main-v6.o - clang main.c -arch xscale -c -o main-xscale.o + ${CC_ARM} main.c -arch xscale -c -o main-xscale.o ${FAIL_IF_BAD_OBJ} main-xscale.o - clang main.c -arch armv7 -c -o main-v7.o + ${CC_ARM} main.c -arch armv7 -c -o main-v7.o ${FAIL_IF_BAD_OBJ} main-v7.o # check V4+V4 -> V4 - ${LD} -r main-v4.o foo-v4.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY} +# ${LD} -r main-v4.o foo-v4.o -o all.o +# ${FAIL_IF_BAD_OBJ} all.o +# otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY} # check V4+V5 -> V5 #${LD} -r main-v4.o foo-v5.o -o all.o @@ -84,9 +86,9 @@ test-arm: #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY} # check V5+V5 -> V5 - ${LD} -r main-v5.o foo-v5.o -o all.o - ${FAIL_IF_BAD_OBJ} all.o - otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY} +# ${LD} -r main-v5.o foo-v5.o -o all.o +# ${FAIL_IF_BAD_OBJ} all.o +# otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY} # check V5+V6 -> V6 #${LD} -r main-v5.o foo-v6.o -o all.o diff --git a/ld64/unit-tests/test-cases/data-in-code/Makefile b/ld64/unit-tests/test-cases/data-in-code/Makefile index 0897726..b1e7fd5 100644 --- a/ld64/unit-tests/test-cases/data-in-code/Makefile +++ b/ld64/unit-tests/test-cases/data-in-code/Makefile @@ -29,9 +29,12 @@ include ${TESTROOT}/include/common.makefile all: ${CC} ${CCFLAGS} -c test.s -o test.o + ${CC} ${CCFLAGS} test.o -dynamiclib -o libtest.dylib + dyldinfo -arch ${ARCH} -data_in_code libtest.dylib| ${FAIL_IF_EMPTY} ${LD} -r -arch ${ARCH} test.o -o test2.o - #nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY} - #${PASS_IFF_GOOD_MACHO} main + ${CC} ${CCFLAGS} test2.o -dynamiclib -o libtest2.dylib + dyldinfo -arch ${ARCH} -data_in_code libtest2.dylib| ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} libtest.dylib clean: - rm -rf test.o test2.o + rm -rf test.o test2.o libtest.dylib libtest2.dylib diff --git a/ld64/unit-tests/test-cases/data-in-code/main.c b/ld64/unit-tests/test-cases/data-in-code/main.c deleted file mode 100644 index 811449a..0000000 --- a/ld64/unit-tests/test-cases/data-in-code/main.c +++ /dev/null @@ -1,49 +0,0 @@ - -#include - -#define DTRACE_STRINGIFY(s) #s -#define DTRACE_TOSTRING(s) DTRACE_STRINGIFY(s) - -#define DTRACE_NOPS \ - "nop" "\n\t" \ - "nop" "\n\t" \ - "nop" "\n\t" - - -#define DTRACE_LAB(p, n) \ - "__dtrace_probe$" DTRACE_TOSTRING(%=__LINE__) DTRACE_STRINGIFY(_##p##___##n) - -#define DTRACE_LABEL(p, n) \ - ".section __DATA, __data\n\t" \ - ".globl " DTRACE_LAB(p, n) "\n\t" \ - DTRACE_LAB(p, n) ":\n\t" ".long 1f""\n\t" \ - ".text" "\n\t" \ - "1:" - -#define DTRACE_CALL(p,n) \ - DTRACE_LABEL(p,n) \ - DTRACE_NOPS - -#define DTRACE_CALL0ARGS(provider, name) \ - __asm volatile ( \ - DTRACE_CALL(provider, name) \ - : \ - : \ - ); - -int deadwood() -{ - DTRACE_CALL0ARGS(__foo__, test2) - return 0; -} - - -int main() { - int a = 1; - - while(a) { - DTRACE_CALL0ARGS(__foo__, test1) - } - - return 0; -} diff --git a/ld64/unit-tests/test-cases/data-in-code/test.s b/ld64/unit-tests/test-cases/data-in-code/test.s index 8dc92b8..19c2448 100644 --- a/ld64/unit-tests/test-cases/data-in-code/test.s +++ b/ld64/unit-tests/test-cases/data-in-code/test.s @@ -4,21 +4,23 @@ _foo: nop nop -l$start$data$1: + .data_region nop nop nop -l$start$jt8$2: + .data_region jt8 nop nop -l$start$jt16$3: +.data_region jt16 nop nop -l$start$code$n4: +.data_region jt32 nop nop - - +.end_data_region + nop + nop + .subsections_via_symbols diff --git a/ld64/unit-tests/test-cases/dso_handle/Makefile b/ld64/unit-tests/test-cases/dso_handle/Makefile index f6c40e4..d91d17f 100644 --- a/ld64/unit-tests/test-cases/dso_handle/Makefile +++ b/ld64/unit-tests/test-cases/dso_handle/Makefile @@ -15,7 +15,7 @@ all: ${FAIL_IF_BAD_MACHO} test ${CC} ${CCFLAGS} test.c -DDSO_DEF=1 -o test-def 2>warnings.txt ${FAIL_IF_BAD_MACHO} test-def - ${CC} ${CCFLAGS} test.c -DDSO_TENT=1 -o test-tent + ${CC} ${CCFLAGS} test.c -DDSO_TENT=1 -o test-tent -Wl,-w ${PASS_IFF_GOOD_MACHO} test-tent diff --git a/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile b/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile index 3fb8a25..e40cc53 100644 --- a/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile +++ b/ld64/unit-tests/test-cases/eh-coalescing-r/Makefile @@ -31,13 +31,12 @@ SHELL = bash # use bash shell so we can redirect just stderr # # comdat warnings in ld -r # -# also use -falign-functions to force an out of order coalesing # run: all all: ${CXX} ${CCXXFLAGS} foo.cxx -c -o foo.o - ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o -falign-functions=32 + ${CXX} ${CCXXFLAGS} bar.cxx -c -o bar.o ${CXX} ${CCXXFLAGS} baz.cxx -c -o baz.o ${LD} -r foo.o bar.o baz.o -o foobarbaz.o 2> warnings.log grep warning warnings.log | ${PASS_IFF_EMPTY} diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s new file mode 100644 index 0000000..b99221b --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAdd.s @@ -0,0 +1,27 @@ + + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo@PAGE +L2: add x0, x0, _foo@PAGEOFF + nop + + .loh AdrpAdd L1, L2 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONST + .const +#endif + +#if FOO_AS_DATA + .data +#endif + +_foo: .long 0 + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s new file mode 100644 index 0000000..cdcaba9 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddLdr.s @@ -0,0 +1,55 @@ + +#ifndef ADDEND + #define ADDEND 0 +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo@PAGE +L2: add x0, x0, _foo@PAGEOFF +#if LOAD_GPR_8 +L3: ldr b1, [x0, #ADDEND] +#elif LOAD_GPR_16 +L3: ldr h1, [x0, #ADDEND] +#elif LOAD_GPR_32 +L3: ldr w1, [x0, #ADDEND] +#elif LOAD_GPR_64 +L3: ldr x1, [x0, #ADDEND] +#elif LOAD_FPR_32 +L3: ldr s1, [x0, #ADDEND] +#elif LOAD_FPR_64 +L3: ldr d1, [x0, #ADDEND] +#elif LOAD_VEC_128 +L3: ldr q1, [x0, #ADDEND] +#endif + nop + + .loh AdrpAddLdr L1, L2, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONSTANT + .literal4 +#endif + +#if FOO_AS_DATA + .data +_makePageOffsetNonZero: .long 0,0,0,0 +#endif + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s new file mode 100644 index 0000000..3e3252a --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpAddStr.s @@ -0,0 +1,44 @@ + +#ifndef ADDEND + #define ADDEND 0 +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo@PAGE +L2: add x0, x0, _foo@PAGEOFF +#if LOAD_GPR_8 +L3: str b1, [x0, #ADDEND] +#elif LOAD_GPR_16 +L3: str h1, [x0, #ADDEND] +#elif LOAD_GPR_32 +L3: str w1, [x0, #ADDEND] +#elif LOAD_GPR_64 +L3: str x1, [x0, #ADDEND] +#elif LOAD_FPR_32 +L3: str s1, [x0, #ADDEND] +#elif LOAD_FPR_64 +L3: str d1, [x0, #ADDEND] +#elif LOAD_VEC_128 +L3: str q1, [x0, #ADDEND] +#endif + nop + + .loh AdrpAddStr L1, L2, L3 + + .data +_makePageOffsetNonZero: .long 0,0,0,0 + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s new file mode 100644 index 0000000..c91fa06 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdr.s @@ -0,0 +1,64 @@ + +#ifndef ADDEND + #define ADDEND +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, _foo ADDEND@PAGE +#if LOAD_GPR_8 +L3: ldr b1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_16 +L3: ldr h1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_32 +L3: ldr w1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_32_S16 +L3: ldrsh w1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_32_S8 +L3: ldrsb w1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64 +L3: ldr x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64_S32 +L3: ldrsw x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64_S16 +L3: ldrsh x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_GPR_64_S8 +L3: ldrsb x1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_FPR_32 +L3: ldr s1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_FPR_64 +L3: ldr d1, [x0, _foo ADDEND@PAGEOFF] +#elif LOAD_VEC_128 +L3: ldr q1, [x0, _foo ADDEND@PAGEOFF] +#endif + nop + + .loh AdrpLdr L1, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONSTANT + .literal4 +#endif + +#if FOO_AS_DATA + .data +_makePageOffsetNonZero: .long 0,0,0,0 +#endif + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s new file mode 100644 index 0000000..40bc180 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGot.s @@ -0,0 +1,30 @@ + +#ifndef TARGET + #define TARGET _malloc +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, TARGET@GOTPAGE +L2: ldr x1, [x0, #TARGET@GOTPAGEOFF] + nop + + .loh AdrpLdrGot L1, L2 + +#if PADDING +_pad: + .space 1100000 +#endif + +_fooCode: + nop + + + .data +_makePageOffsetNonZero: .long 0,0,0,0 + +_fooData: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s new file mode 100644 index 0000000..0f90ff8 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotLdr.s @@ -0,0 +1,56 @@ + +#ifndef TARGET + #define TARGET _foo +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, TARGET@GOTPAGE +L2: ldr x1, [x0, #TARGET@GOTPAGEOFF] +#if LOAD_GPR_8 +L3: ldr b2, [x1] +#elif LOAD_GPR_16 +L3: ldr h2, [x1] +#elif LOAD_GPR_32 +L3: ldr w2, [x1] +#elif LOAD_GPR_64 +L3: ldr x2, [x1] +#elif LOAD_FPR_32 +L3: ldr s2, [x1] +#elif LOAD_FPR_64 +L3: ldr d2, [x1] +#elif LOAD_VEC_128 +L3: ldr q2, [x1] +#endif + nop + + .loh AdrpLdrGotLdr L1, L2, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + +#if FOO_AS_CONST + .const + .align 4 +#endif + +#if FOO_AS_DATA + .data +_makePageOffsetNonZero: .long 0,0,0,0 +#endif + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s new file mode 100644 index 0000000..d3fed03 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/AdrpLdrGotStr.s @@ -0,0 +1,49 @@ + +#ifndef TARGET + #define TARGET _foo +#endif + + .text + .align 2 +_test: + nop +L1: adrp x0, TARGET@GOTPAGE +L2: ldr x1, [x0, #TARGET@GOTPAGEOFF] +#if LOAD_GPR_8 +L3: str b2, [x1] +#elif LOAD_GPR_16 +L3: str h2, [x1] +#elif LOAD_GPR_32 +L3: str w2, [x1] +#elif LOAD_GPR_64 +L3: str x2, [x1] +#elif LOAD_FPR_32 +L3: str s2, [x1] +#elif LOAD_FPR_64 +L3: str d2, [x1] +#elif LOAD_VEC_128 +L3: str q2, [x1] +#endif + nop + + .loh AdrpLdrGotStr L1, L2, L3 + +#if PADDING +_pad: + .space 1100000 +#endif + + .data +_makePageOffsetNonZero: .long 0,0,0,0 + +#if MISALIGN_DATA +_junk: .byte 0 +#endif + +_foo: .long 0 + .long 0 +_8foo8: .long 0 + .long 0 +_16foo16: .long 0 + + diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile b/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile new file mode 100644 index 0000000..de5da0d --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/Makefile @@ -0,0 +1,1418 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that linker does/doesnot apply optimization hints +# + +all: all-${ARCH} + +all-i386: skip +all-x86_64: skip +all-armv6: skip +all-armv7: skip + +all-arm64: AdrpAdd AdrpAddLdr AdrpLdr AdrpLdrGotLdr AdrpAddStr AdrpLdrGotStr AdrpLdrGot + +main.o: + ${CC} ${CCFLAGS} main.s -c -o main.o + +AdrpAdd: main.o + # test ADRP/ADD -> ADR when target is in __TEXT + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd1.o -DFOO_AS_CONST=1 + ${CC} ${CCFLAGS} AdrpAdd1.o main.o -o AdrpAdd1.exe + ${OTOOL} -tV AdrpAdd1.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd1.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAdd1.exe | grep 'add x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD -> ADR when target is in __DATA and main executable + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd2.o -DFOO_AS_DATA=1 + ${CC} ${CCFLAGS} AdrpAdd2.o main.o -o AdrpAdd2.exe + ${OTOOL} -tV AdrpAdd2.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd2.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAdd2.exe | grep 'add x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD -> ADR when target is in __DATA and dylib + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd3.o -DFOO_AS_DATA=1 + ${CC} ${CCFLAGS} AdrpAdd3.o -dynamiclib -o AdrpAdd3.dylib + ${OTOOL} -tV AdrpAdd3.dylib | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd3.dylib | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAdd3.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD !-> when target is in __DATA and dylib in shared region + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd4.o -DFOO_AS_DATA=1 + ${CC} ${CCFLAGS} AdrpAdd4.o -dynamiclib -o AdrpAdd4.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAdd4.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd4.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd4.dylib | grep 'adr x0' | ${FAIL_IF_STDIN} + # test ADRP/ADD !-> when target is in __TEXT but too far + ${CC} ${CCFLAGS} AdrpAdd.s -c -o AdrpAdd5.o -DFOO_AS_CONST=1 -DPADDING + ${CC} ${CCFLAGS} AdrpAdd5.o main.o -o AdrpAdd5.exe + ${OTOOL} -tV AdrpAdd5.exe | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd5.exe | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAdd5.exe | grep 'adr x0' | ${FAIL_IF_STDIN} + + + +AdrpAddLdr: AdrpAddLdr-ldr AdrpAddLdr-far AdrpAddLdr-seg AdrpAddLdr-align AdrpAddLdr-addend + true + +AdrpAddLdr-ldr: main.o + # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g8.o main.o -o AdrpAddLdr-ldr-g8.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADR/LDR when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g16.o main.o -o AdrpAddLdr-ldr-g16.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g32.o main.o -o AdrpAddLdr-ldr-g32.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'ldr w1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-g64.o main.o -o AdrpAddLdr-ldr-g64.exe + ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-f32.o main.o -o AdrpAddLdr-ldr-f32.exe + ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'ldr s1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-f64.o main.o -o AdrpAddLdr-ldr-f64.exe + ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'ldr d1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-ldr-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddLdr-ldr-v128.o main.o -o AdrpAddLdr-ldr-v128.exe + ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'ldr q1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-ldr-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddLdr-far: main.o + # test ADRP/ADD/LD -> ADRP/LD when target is far for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g8.o main.o -o AdrpAddLdr-far-g8.exe + ${OTOOL} -tV AdrpAddLdr-far-g8.exe | grep 'ldr b1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g16.o main.o -o AdrpAddLdr-far-g16.exe + ${OTOOL} -tV AdrpAddLdr-far-g16.exe | grep 'ldr h1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g32.o main.o -o AdrpAddLdr-far-g32.exe + ${OTOOL} -tV AdrpAddLdr-far-g32.exe | grep 'ldr w1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-g64.o main.o -o AdrpAddLdr-far-g64.exe + ${OTOOL} -tV AdrpAddLdr-far-g64.exe | grep 'ldr x1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-f32.o main.o -o AdrpAddLdr-far-f32.exe + ${OTOOL} -tV AdrpAddLdr-far-f32.exe | grep 'ldr s1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-f64.o main.o -o AdrpAddLdr-far-f64.exe + ${OTOOL} -tV AdrpAddLdr-far-f64.exe | grep 'ldr d1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING + ${CC} ${CCFLAGS} AdrpAddLdr-far-v128.o main.o -o AdrpAddLdr-far-v128.exe + ${OTOOL} -tV AdrpAddLdr-far-v128.exe | grep 'ldr q1, \[x0, ' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-far-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + + +AdrpAddLdr-align: main.o + # test ADRP/ADD/LD -> ADR/LD when target is for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g8.o main.o -o AdrpAddLdr-align-g8.exe + ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g16.o main.o -o AdrpAddLdr-align-g16.exe + ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'ldr h1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g32.o main.o -o AdrpAddLdr-align-g32.exe + ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-g32.exe | grep 'ldr w1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-g64.o main.o -o AdrpAddLdr-align-g64.exe + ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-g64.exe | grep 'ldr x1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-f32.o main.o -o AdrpAddLdr-align-f32.exe + ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-f32.exe | grep 'ldr s1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-f64.o main.o -o AdrpAddLdr-align-f64.exe + ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-f64.exe | grep 'ldr d1, \[x0\]' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADR/LD when target is not aligned for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-align-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddLdr-align-v128.o main.o -o AdrpAddLdr-align-v128.exe + ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-align-v128.exe | grep 'ldr q1, \[x0\]' | ${FAIL_IF_EMPTY} + + + +AdrpAddLdr-seg: + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g8.o -dynamiclib -o AdrpAddLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g8.dylib | grep 'ldr b1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g16.o -dynamiclib -o AdrpAddLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g16.dylib | grep 'ldr h1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g32.o -dynamiclib -o AdrpAddLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g32.dylib | grep 'ldr w1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-g64.o -dynamiclib -o AdrpAddLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-g64.dylib | grep 'ldr x1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-f32.o -dynamiclib -o AdrpAddLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-f32.dylib | grep 'ldr s1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segment for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-f64.o -dynamiclib -o AdrpAddLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-f64.dylib | grep 'ldr d1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/LD -> ADRP/LD when target is in movable segmentfor 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddLdr-seg-v128.o -dynamiclib -o AdrpAddLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-seg-v128.dylib | grep 'ldr q1, \[x0,' | ${FAIL_IF_EMPTY} + + + +AdrpAddLdr-addend: + # test ADRP/ADD/LD -> ADRP/LD when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g8.o main.o -o AdrpAddLdr-addend-g8.exe + ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'ldr\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> ADRP/LD when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g16.o main.o -o AdrpAddLdr-addend-g16.exe + ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'ldr\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g32.o main.o -o AdrpAddLdr-addend-g32.exe + ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'ldr w1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-g64.o main.o -o AdrpAddLdr-addend-g64.exe + ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'ldr x1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-f32.o main.o -o AdrpAddLdr-addend-f32.exe + ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'ldr s1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-f64.o main.o -o AdrpAddLdr-addend-f64.exe + ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'ldr d1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/LD -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddLdr.s -c -o AdrpAddLdr-addend-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DADDEND=16 + ${CC} ${CCFLAGS} AdrpAddLdr-addend-v128.o main.o -o AdrpAddLdr-addend-v128.exe + ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'ldr q1, _16foo16' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpAddLdr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + + + +AdrpLdr: AdrpLdr-ldr AdrpLdr-seg AdrpLdr-addend + true + +AdrpLdr-ldr: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g8.o main.o -o AdrpLdr-ldr-g8.exe + ${OTOOL} -tV AdrpLdr-ldr-g8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g8.exe | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g16.o main.o -o AdrpLdr-ldr-g16.exe + ${OTOOL} -tV AdrpLdr-ldr-g16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g16.exe | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g32.o main.o -o AdrpLdr-ldr-g32.exe + ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'ldr w1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR left untouched when target is in __TEXT for 32-bit load from signed 8-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32s8.o -DFOO_AS_CONST -DLOAD_GPR_32_S8 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g32s8.o main.o -o AdrpLdr-ldr-g32s8.exe + ${OTOOL} -tV AdrpLdr-ldr-g32s8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g32s8.exe | grep 'ldrsb\tw1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load from signed 16-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g32s16.o -DFOO_AS_CONST -DLOAD_GPR_32_S16 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g32s16.o main.o -o AdrpLdr-ldr-g32s16.exe + ${OTOOL} -tV AdrpLdr-ldr-g32s16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g32s16.exe | grep 'ldrsh\tw1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64.o main.o -o AdrpLdr-ldr-g64.exe + ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'ldr x1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR left untouched when target is in __TEXT for 64-bit load from signed 8-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s8.o -DFOO_AS_CONST -DLOAD_GPR_64_S8 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s8.o main.o -o AdrpLdr-ldr-g64s8.exe + ${OTOOL} -tV AdrpLdr-ldr-g64s8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64s8.exe | grep 'ldrsb\tx1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 64-bit load from signed 16-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s16.o -DFOO_AS_CONST -DLOAD_GPR_64_S16 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s16.o main.o -o AdrpLdr-ldr-g64s16.exe + ${OTOOL} -tV AdrpLdr-ldr-g64s16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64s16.exe | grep 'ldrsh\tx1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load from signed 32-bit value + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-g64s32.o -DFOO_AS_CONST -DLOAD_GPR_64_S32 + ${CC} ${CCFLAGS} AdrpLdr-ldr-g64s32.o main.o -o AdrpLdr-ldr-g64s32.exe + ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'ldrsw x1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-g64s32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdr-ldr-f32.o main.o -o AdrpLdr-ldr-f32.exe + ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'ldr s1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdr-ldr-f64.o main.o -o AdrpLdr-ldr-f64.exe + ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'ldr d1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-ldr-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdr-ldr-v128.o main.o -o AdrpLdr-ldr-v128.exe + ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'ldr q1, _foo' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-ldr-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + +AdrpLdr-seg: + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdr-seg-g8.o -dynamiclib -o AdrpLdr-seg-g8.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g8.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g8.dylib | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdr-seg-g16.o -dynamiclib -o AdrpLdr-seg-g16.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g16.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g16.dylib | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdr-seg-g32.o -dynamiclib -o AdrpLdr-seg-g32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g32.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g32.dylib | grep 'ldr\tw1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdr-seg-g64.o -dynamiclib -o AdrpLdr-seg-g64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-g64.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-g64.dylib | grep 'ldr\tx1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdr-seg-f32.o -dynamiclib -o AdrpLdr-seg-f32.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-f32.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-f32.dylib | grep 'ldr\ts1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdr-seg-f64.o -dynamiclib -o AdrpLdr-seg-f64.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-f64.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-f64.dylib | grep 'ldr\td1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-seg-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdr-seg-v128.o -dynamiclib -o AdrpLdr-seg-v128.dylib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpLdr-seg-v128.dylib | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-seg-v128.dylib | grep 'ldr\tq1, \[x0,' | ${FAIL_IF_EMPTY} + +AdrpLdr-addend: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g8.o -DFOO_AS_DATA -DLOAD_GPR_8 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g8.o main.o -o AdrpLdr-addend-g8.exe + ${OTOOL} -tV AdrpLdr-addend-g8.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g8.exe | grep 'ldr\tb1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR left untouched when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g16.o -DFOO_AS_DATA -DLOAD_GPR_16 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g16.o main.o -o AdrpLdr-addend-g16.exe + ${OTOOL} -tV AdrpLdr-addend-g16.exe | grep 'adrp\tx0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g16.exe | grep 'ldr\th1, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g32.o -DFOO_AS_DATA -DLOAD_GPR_32 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g32.o main.o -o AdrpLdr-addend-g32.exe + ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'ldr w1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-g64.o -DFOO_AS_DATA -DLOAD_GPR_64 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-g64.o main.o -o AdrpLdr-addend-g64.exe + ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'ldr x1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-f32.o -DFOO_AS_DATA -DLOAD_FPR_32 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-f32.o main.o -o AdrpLdr-addend-f32.exe + ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'ldr s1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-f64.o -DFOO_AS_DATA -DLOAD_FPR_64 -DADDEND=+8 + ${CC} ${CCFLAGS} AdrpLdr-addend-f64.o main.o -o AdrpLdr-addend-f64.exe + ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'ldr d1, _8foo8' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/LDR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdr.s -c -o AdrpLdr-addend-v128.o -DFOO_AS_DATA -DLOAD_VEC_128 -DADDEND=+16 + ${CC} ${CCFLAGS} AdrpLdr-addend-v128.o main.o -o AdrpLdr-addend-v128.exe + ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'ldr q1, _16foo16' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'adrp x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpLdrGotLdr: AdrpLdrGotLdr-extern AdrpLdrGotLdr-externfargot AdrpLdrGotLdr-near AdrpLdrGotLdr-far AdrpLdrGotLdr-nearunaligned AdrpLdrGotLdr-farunaligned + true + +AdrpLdrGotLdr-extern: main.o + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g8.o main.o -o AdrpLdrGotLdr-extern-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g16.o main.o -o AdrpLdrGotLdr-extern-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g32.o main.o -o AdrpLdrGotLdr-extern-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-g64.o main.o -o AdrpLdrGotLdr-extern-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-f32.o main.o -o AdrpLdrGotLdr-extern-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-f64.o main.o -o AdrpLdrGotLdr-extern-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/LDR when GOT slot is close for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotLdr-extern-v128.o main.o -o AdrpLdrGotLdr-extern-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-extern-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-externfargot: main.o + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g8.o main.o -o AdrpLdrGotLdr-externfargot-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g16.o main.o -o AdrpLdrGotLdr-externfargot-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot farfor 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g32.o main.o -o AdrpLdrGotLdr-externfargot-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-g64.o main.o -o AdrpLdrGotLdr-externfargot-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-f32.o main.o -o AdrpLdrGotLdr-externfargot-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-f64.o main.o -o AdrpLdrGotLdr-externfargot-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-externfargot-v128.o main.o -o AdrpLdrGotLdr-externfargot-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-externfargot-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-near: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g8.o main.o -o AdrpLdrGotLdr-near-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-near-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g16.o main.o -o AdrpLdrGotLdr-near-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-near-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g32.o main.o -o AdrpLdrGotLdr-near-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g32.exe | grep 'ldr\tw2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-g64.o main.o -o AdrpLdrGotLdr-near-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-g64.exe | grep 'ldr\tx2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-f32.o main.o -o AdrpLdrGotLdr-near-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f32.exe | grep 'ldr\ts2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-f64.o main.o -o AdrpLdrGotLdr-near-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-f64.exe | grep 'ldr\td2, _foo' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-near-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdrGotLdr-near-v128.o main.o -o AdrpLdrGotLdr-near-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-near-v128.exe | grep 'ldr\tq2, _foo' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-nearunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g8.o main.o -o AdrpLdrGotLdr-nearunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g8.exe | grep 'ldr\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g16.o main.o -o AdrpLdrGotLdr-nearunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g32.o main.o -o AdrpLdrGotLdr-nearunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-g64.o main.o -o AdrpLdrGotLdr-nearunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-f32.o main.o -o AdrpLdrGotLdr-nearunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-f64.o main.o -o AdrpLdrGotLdr-nearunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-nearunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotLdr-nearunaligned-v128.o main.o -o AdrpLdrGotLdr-nearunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-nearunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-far: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g8.o main.o -o AdrpLdrGotLdr-far-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g8.exe | grep 'ldr\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g16.o main.o -o AdrpLdrGotLdr-far-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g16.exe | grep 'ldr\th2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g32.o main.o -o AdrpLdrGotLdr-far-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g32.exe | grep 'ldr\tw2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-g64.o main.o -o AdrpLdrGotLdr-far-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-g64.exe | grep 'ldr\tx2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-f32.o main.o -o AdrpLdrGotLdr-far-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-f32.exe | grep 'ldr\ts2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-f64.o main.o -o AdrpLdrGotLdr-far-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-f64.exe | grep 'ldr\td2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-far-v128.o main.o -o AdrpLdrGotLdr-far-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-far-v128.exe | grep 'ldr\tq2, \[x0,' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotLdr-farunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g8.o main.o -o AdrpLdrGotLdr-farunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g8.exe | grep 'ldr\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g16.o main.o -o AdrpLdrGotLdr-farunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g16.exe | grep 'ldr\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g32.o main.o -o AdrpLdrGotLdr-farunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g32.exe | grep 'ldr\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-g64.o main.o -o AdrpLdrGotLdr-farunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-g64.exe | grep 'ldr\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-f32.o main.o -o AdrpLdrGotLdr-farunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f32.exe | grep 'ldr\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-f64.o main.o -o AdrpLdrGotLdr-farunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-f64.exe | grep 'ldr\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotLdr.s -c -o AdrpLdrGotLdr-farunaligned-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotLdr-farunaligned-v128.o main.o -o AdrpLdrGotLdr-farunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotLdr-farunaligned-v128.exe | grep 'ldr\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpAddStr: AdrpAddStr-base AdrpAddStr-seg AdrpAddStr-align AdrpAddStr-addend AdrpAddStr-faraddend + true + +AdrpAddStr-base: main.o + # test ADRP/ADD/STR -> ADR/STR for 8-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g8.o -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddStr-base-g8.o main.o -o AdrpAddStr-base-g8.exe + ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 16-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g16.o -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddStr-base-g16.o main.o -o AdrpAddStr-base-g16.exe + ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'str\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 32-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g32.o -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-base-g32.o main.o -o AdrpAddStr-base-g32.exe + ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'str\tw1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 64-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-g64.o -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-base-g64.o main.o -o AdrpAddStr-base-g64.exe + ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'str\tx1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 32-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-f32.o -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-base-f32.o main.o -o AdrpAddStr-base-f32.exe + ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'str\ts1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 64-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-f64.o -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-base-f64.o main.o -o AdrpAddStr-base-f64.exe + ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'str\td1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR for 128-bit vec store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-base-v128.o -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddStr-base-v128.o main.o -o AdrpAddStr-base-v128.exe + ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'str\tq1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-base-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddStr-seg: main.o + # test ADRP/ADD/STR -> ADRP/STR for 8-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g8.o -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g8.o -o AdrpAddStr-seg-g8.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'str\tb1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g8.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 16-bit v + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g16.o -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g16.o -o AdrpAddStr-seg-g16.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'str\th1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g16.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 32-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g32.o -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g32.o -o AdrpAddStr-seg-g32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'str\tw1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 64-bit store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-g64.o -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-seg-g64.o -o AdrpAddStr-seg-g64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'str\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-g64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 32-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-f32.o -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpAddStr-seg-f32.o -o AdrpAddStr-seg-f32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'str\ts1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f32.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 64-bit fp store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-f64.o -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpAddStr-seg-f64.o -o AdrpAddStr-seg-f64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'str\td1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-f64.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/STR for 128-bit vec store + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-seg-v128.o -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpAddStr-seg-v128.o -o AdrpAddStr-seg-v128.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'str\tq1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-seg-v128.dylib | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddStr-align: main.o + # test ADRP/ADD/STR -> ADR/STR when target is for 8-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g8.o main.o -o AdrpAddStr-align-g8.exe + ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 16-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g16.o main.o -o AdrpAddStr-align-g16.exe + ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'str h1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 32-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g32.o main.o -o AdrpAddStr-align-g32.exe + ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'str w1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 64-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-g64.o main.o -o AdrpAddStr-align-g64.exe + ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'str x1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-f32.o main.o -o AdrpAddStr-align-f32.exe + ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'str s1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-f64.o main.o -o AdrpAddStr-align-f64.exe + ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'str d1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADR/STR when target is not aligned for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-align-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpAddStr-align-v128.o main.o -o AdrpAddStr-align-v128.exe + ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'adr x0,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'str q1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-align-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + + +AdrpAddStr-addend: + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g8.o -DLOAD_GPR_8 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g8.o main.o -o AdrpAddStr-addend-g8.exe + ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'str\tb1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g8.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g16.o -DLOAD_GPR_16 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g16.o main.o -o AdrpAddStr-addend-g16.exe + ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'str\th1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g16.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g32.o -DLOAD_GPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g32.o main.o -o AdrpAddStr-addend-g32.exe + ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'str\tw1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-g64.o -DLOAD_GPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-g64.o main.o -o AdrpAddStr-addend-g64.exe + ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'str\tx1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-g64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-f32.o -DLOAD_FPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-f32.o main.o -o AdrpAddStr-addend-f32.exe + ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'str\ts1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f32.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-f64.o -DLOAD_FPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-addend-f64.o main.o -o AdrpAddStr-addend-f64.exe + ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'str\td1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-f64.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-addend-v128.o -DLOAD_VEC_128 -DADDEND=16 + ${CC} ${CCFLAGS} AdrpAddStr-addend-v128.o main.o -o AdrpAddStr-addend-v128.exe + ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'adr x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'str\tq1, \[x0\]' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-addend-v128.exe | grep 'add x0' | ${FAIL_IF_STDIN} + + +AdrpAddStr-faraddend: + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g8.o -DLOAD_GPR_8 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g8.o -o AdrpAddStr-faraddend-g8.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g8.dylib | grep 'str\tb1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> ADRP/LD when target is in __TEXT for 16-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g16.o -DLOAD_GPR_16 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g16.o -o AdrpAddStr-faraddend-g16.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g16.dylib | grep 'str\th1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g32.o -DLOAD_GPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g32.o -o AdrpAddStr-faraddend-g32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g32.dylib | grep 'str\tw1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-g64.o -DLOAD_GPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-g64.o -o AdrpAddStr-faraddend-g64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-g64.dylib | grep 'str\tx1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 32-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-f32.o -DLOAD_FPR_32 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-f32.o -o AdrpAddStr-faraddend-f32.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f32.dylib | grep 'str\ts1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 64-bit fp load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-f64.o -DLOAD_FPR_64 -DADDEND=8 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-f64.o -o AdrpAddStr-faraddend-f64.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-f64.dylib | grep 'str\td1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/ADD/STR -> LDR when target is in __TEXT for 128-bit vec load + ${CC} ${CCFLAGS} AdrpAddStr.s -c -o AdrpAddStr-faraddend-v128.o -DLOAD_VEC_128 -DADDEND=16 + ${CC} ${CCFLAGS} AdrpAddStr-faraddend-v128.o -o AdrpAddStr-faraddend-v128.dylib -dynamiclib -install_name /usr/lib/libjunk.dylib + ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'adrp x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'add x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpAddStr-faraddend-v128.dylib | grep 'str\tq1, \[x0' | ${FAIL_IF_EMPTY} + + + +AdrpLdrGotStr: AdrpLdrGotStr-extern AdrpLdrGotStr-externfargot AdrpLdrGotStr-near AdrpLdrGotStr-far AdrpLdrGotStr-nearunaligned AdrpLdrGotStr-farunaligned + true + +AdrpLdrGotStr-extern: main.o + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g8.o main.o -o AdrpLdrGotStr-extern-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g16.o main.o -o AdrpLdrGotStr-extern-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g32.o main.o -o AdrpLdrGotStr-extern-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-g64.o main.o -o AdrpLdrGotStr-extern-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-f32.o main.o -o AdrpLdrGotStr-extern-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-f64.o main.o -o AdrpLdrGotStr-extern-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR/STR when GOT slot is close for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-extern-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc + ${CC} ${CCFLAGS} AdrpLdrGotStr-extern-v128.o main.o -o AdrpLdrGotStr-extern-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'ldr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-extern-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-externfargot: main.o + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g8.o main.o -o AdrpLdrGotStr-externfargot-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g16.o main.o -o AdrpLdrGotStr-externfargot-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot farfor 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g32.o main.o -o AdrpLdrGotStr-externfargot-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-g64.o main.o -o AdrpLdrGotStr-externfargot-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-f32.o main.o -o AdrpLdrGotStr-externfargot-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-f64.o main.o -o AdrpLdrGotStr-externfargot-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-externfargot-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DTARGET=_malloc -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-externfargot-v128.o main.o -o AdrpLdrGotStr-externfargot-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-externfargot-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-near: main.o + # test ADRP/LDR/STR -> ADR/STR when target close for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g8.o -DLOAD_GPR_8 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g8.o main.o -o AdrpLdrGotStr-near-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-near-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g16.o -DLOAD_GPR_16 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g16.o main.o -o AdrpLdrGotStr-near-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-near-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g32.o -DLOAD_GPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g32.o main.o -o AdrpLdrGotStr-near-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-g64.o -DLOAD_GPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-g64.o main.o -o AdrpLdrGotStr-near-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-f32.o -DLOAD_FPR_32 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-f32.o main.o -o AdrpLdrGotStr-near-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-f64.o -DLOAD_FPR_64 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-f64.o main.o -o AdrpLdrGotStr-near-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/STR -> ADR/STR when target close for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-near-v128.o -DLOAD_VEC_128 + ${CC} ${CCFLAGS} AdrpLdrGotStr-near-v128.o main.o -o AdrpLdrGotStr-near-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-near-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-nearunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g8.o main.o -o AdrpLdrGotStr-nearunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g8.exe | grep 'str\tb2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g16.o main.o -o AdrpLdrGotStr-nearunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g32.o main.o -o AdrpLdrGotStr-nearunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-g64.o main.o -o AdrpLdrGotStr-nearunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-f32.o main.o -o AdrpLdrGotStr-nearunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-f64.o main.o -o AdrpLdrGotStr-nearunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-nearunaligned-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA + ${CC} ${CCFLAGS} AdrpLdrGotStr-nearunaligned-v128.o main.o -o AdrpLdrGotStr-nearunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'adr\tx1,' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-nearunaligned-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-far: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g8.o -DFOO_AS_CONST -DLOAD_GPR_8 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g8.o main.o -o AdrpLdrGotStr-far-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g8.exe | grep 'str\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g16.o -DFOO_AS_CONST -DLOAD_GPR_16 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g16.o main.o -o AdrpLdrGotStr-far-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g16.exe | grep 'str\th2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g32.o -DFOO_AS_CONST -DLOAD_GPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g32.o main.o -o AdrpLdrGotStr-far-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g32.exe | grep 'str\tw2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-g64.o -DFOO_AS_CONST -DLOAD_GPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-g64.o main.o -o AdrpLdrGotStr-far-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-g64.exe | grep 'str\tx2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-f32.o -DFOO_AS_CONST -DLOAD_FPR_32 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-f32.o main.o -o AdrpLdrGotStr-far-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-f32.exe | grep 'str\ts2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-f64.o -DFOO_AS_CONST -DLOAD_FPR_64 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-f64.o main.o -o AdrpLdrGotStr-far-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-f64.exe | grep 'str\td2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-far-v128.o -DFOO_AS_CONST -DLOAD_VEC_128 -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-far-v128.o main.o -o AdrpLdrGotStr-far-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'adrp\tx0' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-far-v128.exe | grep 'str\tq2, \[x0,' | ${FAIL_IF_EMPTY} + + +AdrpLdrGotStr-farunaligned: main.o + # test ADRP/LDR left untouched when target is in __TEXT for 8-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g8.o -DLOAD_GPR_8 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g8.o main.o -o AdrpLdrGotStr-farunaligned-g8.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'add\t' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g8.exe | grep 'str\tb2, \[x0,' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 16-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g16.o -DLOAD_GPR_16 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g16.o main.o -o AdrpLdrGotStr-farunaligned-g16.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g16.exe | grep 'str\th2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -> LDR literal when target close for 32-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g32.o -DLOAD_GPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g32.o main.o -o AdrpLdrGotStr-farunaligned-g32.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g32.exe | grep 'str\tw2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-g64.o -DLOAD_GPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-g64.o main.o -o AdrpLdrGotStr-farunaligned-g64.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-g64.exe | grep 'str\tx2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 32-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-f32.o -DLOAD_FPR_32 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-f32.o main.o -o AdrpLdrGotStr-farunaligned-f32.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f32.exe | grep 'str\ts2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR left untouched when target is external and GOT slot far for 64-bit fp load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-f64.o -DLOAD_FPR_64 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-f64.o main.o -o AdrpLdrGotStr-farunaligned-f64.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-f64.exe | grep 'str\td2, \[x1\]' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR/LDR -left untouched when target is external and GOT slot far for 128-bit vec load + ${CC} ${CCFLAGS} AdrpLdrGotStr.s -c -o AdrpLdrGotStr-farunaligned-v128.o -DLOAD_VEC_128 -DMISALIGN_DATA -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGotStr-farunaligned-v128.o main.o -o AdrpLdrGotStr-farunaligned-v128.exe + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'add\t' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGotStr-farunaligned-v128.exe | grep 'str\tq2, \[x1\]' | ${FAIL_IF_EMPTY} + + +AdrpLdrGot: main.o + # test ADRP/LDR left untouched when target is extern and GOT is far + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-externfar.o -DPADDING + ${CC} ${CCFLAGS} AdrpLdrGot-externfar.o main.o -o AdrpLdrGot-externfar.exe + ${OTOOL} -tV AdrpLdrGot-externfar.exe | grep 'adrp' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGot-externfar.exe | grep 'ldr\tx1, \[x0' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> NOP/LDR literal when target is exern and GOT is near + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-externnear.o + ${CC} ${CCFLAGS} AdrpLdrGot-externnear.o main.o -o AdrpLdrGot-externnear.exe + ${OTOOL} -tV AdrpLdrGot-externnear.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGot-externnear.exe | grep 'ldr\tx1' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> ADR when target is local, near code + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localnear.o -DTARGET=_fooCode + ${CC} ${CCFLAGS} AdrpLdrGot-localnear.o main.o -o AdrpLdrGot-localnear.exe + ${OTOOL} -tV AdrpLdrGot-localnear.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGot-localnear.exe | grep 'adr\tx1' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> ADR when target is local, near data + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localnear2.o -DTARGET=_fooData + ${CC} ${CCFLAGS} AdrpLdrGot-localnear2.o main.o -o AdrpLdrGot-localnear2.exe + ${OTOOL} -tV AdrpLdrGot-localnear2.exe | grep 'adrp' | ${FAIL_IF_STDIN} + ${OTOOL} -tV AdrpLdrGot-localnear2.exe | grep 'adr\tx1' | ${FAIL_IF_EMPTY} + + # test ADRP/LDR -> ADR when target is local, far code + ${CC} ${CCFLAGS} AdrpLdrGot.s -c -o AdrpLdrGot-localfar.o -DTARGET=_fooCode -DPADDING=1 + ${CC} ${CCFLAGS} AdrpLdrGot-localfar.o main.o -o AdrpLdrGot-localfar.exe + ${OTOOL} -tV AdrpLdrGot-localfar.exe | grep 'adrp\tx1' | ${FAIL_IF_EMPTY} + ${OTOOL} -tV AdrpLdrGot-localfar.exe | grep 'add\tx1' | ${FAIL_IF_EMPTY} + + + +clean: + rm -f *.o *.exe *.dylib diff --git a/ld64/unit-tests/test-cases/linker-optimization-hints/main.s b/ld64/unit-tests/test-cases/linker-optimization-hints/main.s new file mode 100644 index 0000000..762b4e4 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker-optimization-hints/main.s @@ -0,0 +1,6 @@ + + .text + .align 2 + .globl _main +_main: ret lr + diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/linker_options-framework/Makefile similarity index 67% copy from ld64/unit-tests/test-cases/branch-islands/Makefile copy to ld64/unit-tests/test-cases/linker_options-framework/Makefile index 8e1870b..3ac482e 100644 --- a/ld64/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/linker_options-framework/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008-2009 Apple Inc. All rights reserved. +# Copyright (c) 2013 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,21 +23,21 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - # -# Simple test for branch islands +# Check linker options work for -framework # run: all - all: - # Verify that we fail if there is no valid place to insert branch islands. - ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} - - ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} - ${PASS_IFF_GOOD_MACHO} hello - + 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 hello + rm -rf main libfoo.dylib main.o main2.o Foo.framework + diff --git a/ld64/unit-tests/test-cases/linker_options-framework/foo.c b/ld64/unit-tests/test-cases/linker_options-framework/foo.c new file mode 100644 index 0000000..e704870 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-framework/foo.c @@ -0,0 +1,3 @@ + + +void foo() { } diff --git a/ld64/unit-tests/test-cases/linker_options-framework/main.c b/ld64/unit-tests/test-cases/linker_options-framework/main.c new file mode 100644 index 0000000..4f56fe0 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-framework/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/linker_options-library/Makefile similarity index 65% copy from ld64/unit-tests/test-cases/branch-islands/Makefile copy to ld64/unit-tests/test-cases/linker_options-library/Makefile index 8e1870b..93697e5 100644 --- a/ld64/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/linker_options-library/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008-2009 Apple Inc. All rights reserved. +# Copyright (c) 2013 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,21 +23,22 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - # -# Simple test for branch islands +# Check linker options work for -l # run: all - all: - # Verify that we fail if there is no valid place to insert branch islands. - ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} - - ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} - ${PASS_IFF_GOOD_MACHO} hello - + ${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 hello + rm -f main libfoo.dylib main.o main2.o bar.o libbar.a + diff --git a/ld64/unit-tests/test-cases/linker_options-library/bar.c b/ld64/unit-tests/test-cases/linker_options-library/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/ld64/unit-tests/test-cases/linker_options-library/foo.c b/ld64/unit-tests/test-cases/linker_options-library/foo.c new file mode 100644 index 0000000..e704870 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library/foo.c @@ -0,0 +1,3 @@ + + +void foo() { } diff --git a/ld64/unit-tests/test-cases/linker_options-library/main.c b/ld64/unit-tests/test-cases/linker_options-library/main.c new file mode 100644 index 0000000..594cf17 --- /dev/null +++ b/ld64/unit-tests/test-cases/linker_options-library/main.c @@ -0,0 +1,10 @@ + +extern void foo(); +extern void bar(); + +int main() +{ + foo(); + bar(); + return 0; +} diff --git a/ld64/unit-tests/test-cases/data-in-code/Makefile b/ld64/unit-tests/test-cases/lto-dynamic_export/Makefile similarity index 71% copy from ld64/unit-tests/test-cases/data-in-code/Makefile copy to ld64/unit-tests/test-cases/lto-dynamic_export/Makefile index 0897726..416c5a9 100644 --- a/ld64/unit-tests/test-cases/data-in-code/Makefile +++ b/ld64/unit-tests/test-cases/lto-dynamic_export/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2011 Apple Inc. All rights reserved. +# Copyright (c) 2012 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,14 +24,19 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile # -# Test that L$start$.. labels are tracked +# verify -preload -pie produces relocations # -all: - ${CC} ${CCFLAGS} -c test.s -o test.o - ${LD} -r -arch ${ARCH} test.o -o test2.o - #nm main | grep _dtrace_probe | ${FAIL_IF_EMPTY} - #${PASS_IFF_GOOD_MACHO} main +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 -rf test.o test2.o + rm main.o main main-de diff --git a/ld64/unit-tests/test-cases/lto-dynamic_export/main.c b/ld64/unit-tests/test-cases/lto-dynamic_export/main.c new file mode 100644 index 0000000..bb3ced0 --- /dev/null +++ b/ld64/unit-tests/test-cases/lto-dynamic_export/main.c @@ -0,0 +1,14 @@ + +__attribute__((visibility("hidden"))) +void foo() { } + +void bar() { } + + +int main() +{ + foo(); + bar(); + + return 0; +} \ No newline at end of file diff --git a/ld64/unit-tests/test-cases/objc-abi/Makefile b/ld64/unit-tests/test-cases/objc-abi/Makefile index d7eb660..d563cd9 100644 --- a/ld64/unit-tests/test-cases/objc-abi/Makefile +++ b/ld64/unit-tests/test-cases/objc-abi/Makefile @@ -35,12 +35,12 @@ endif run: ${ALL} all: - ${PASS_IFF_GOOD_MACHO} /usr/bin/true + ${PASS_IFF} /usr/bin/true all-i386: - ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 + ${CC} ${CCFLAGS} test.m -framework Foundation -o test1 -Wno-objc-root-class size -l test1 | grep __image_info | ${FAIL_IF_EMPTY} - ${CC} ${CCFLAGS} test.m -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2 + ${CC} ${CCFLAGS} test.m -Wno-objc-root-class -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -framework Foundation -o test2 size -l test2 | grep __objc_imageinfo | ${FAIL_IF_EMPTY} ${PASS_IFF_GOOD_MACHO} test2 diff --git a/ld64/unit-tests/test-cases/objc-abi/test.m b/ld64/unit-tests/test-cases/objc-abi/test.m index 8b4a295..292c456 100644 --- a/ld64/unit-tests/test-cases/objc-abi/test.m +++ b/ld64/unit-tests/test-cases/objc-abi/test.m @@ -1,5 +1,5 @@ -@interface Foo +@interface Foo @end @implementation Foo diff --git a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile index 2bbc5dd..7dcafac 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize-load/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch + OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ endif all: all-${ARCH} diff --git a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile index c395988..de4ea0c 100644 --- a/ld64/unit-tests/test-cases/objc-category-optimize/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-optimize/Makefile @@ -29,7 +29,7 @@ include ${TESTROOT}/include/common.makefile OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch + OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ endif ifeq ($(ARCH),arm) diff --git a/ld64/unit-tests/test-cases/objc-category-warning/Makefile b/ld64/unit-tests/test-cases/objc-category-warning/Makefile index 5481ba0..31d841b 100644 --- a/ld64/unit-tests/test-cases/objc-category-warning/Makefile +++ b/ld64/unit-tests/test-cases/objc-category-warning/Makefile @@ -30,7 +30,7 @@ SHELL = bash # use bash shell so we can redirect just stderr OPTIONS = ifeq ($(ARCH),i386) - OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2 -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fobjc-legacy-dispatch + OPTIONS = -mios-simulator-version-min=6.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/ endif all: all-${FILEARCH} diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile index 67e04a3..13d1743 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/Makefile +++ b/ld64/unit-tests/test-cases/objc-gc-checks/Makefile @@ -63,70 +63,70 @@ test-macosx: ${FAIL_IF_BAD_OBJ} bar-gc-only.o # check RR + RR -> RR - ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo.o bar.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib # check GC/RR + GC/RR -> GC/RR - ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x2 | ${FAIL_IF_EMPTY} # check GC + GC -> GC - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} # check RR + GC/RR -> RR - ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} # check GC/RR + RR -> RR - ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} bar-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x[26] | ${FAIL_IF_STDIN} # check GC + GC/RR -> GC - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation ${FAIL_IF_BAD_MACHO} libfoobar.dylib otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x6 | ${FAIL_IF_EMPTY} # check RR + GC -> error - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib 2> fail.log + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo.o bar-gc-only.o runtime.c -dynamiclib -o libfoobar.dylib -framework Foundation 2> fail.log # check cmd line GC/RR, GC/RR + RR -> error - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc 2> fail.log + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} foo-gc.o foo.o runtime.c -dynamiclib -o libfoobar.dylib -Wl,-objc_gc -framework Foundation 2> fail.log # check GC/RR + compaction - ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc.o bar-gc.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x12 | ${FAIL_IF_EMPTY} # check GC + compaction - ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib + ${CC} ${CCFLAGS} foo-gc-only.o bar-gc-only.o runtime.c -dynamiclib -Wl,-objc_gc_compaction -o libfoobar.dylib -framework Foundation otool -o libfoobar.dylib | grep -A4 _image | grep flags | grep 0x16 | ${FAIL_IF_EMPTY} # none + GC/RR-dylib -> none - ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + ${CC} ${CCFLAGS} foo-gc.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # none + GC-dylib -> none - ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # none + RR-dylib -> none - ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib - ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib + ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${CC} ${CCFLAGS} none.c libfoo.dylib -dynamiclib -o libnone.dylib -framework Foundation size -l libnone.dylib | grep ${IMAGE_INFO} | ${FAIL_IF_STDIN} # check RR + GC-dylib -> error - ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log + ${CC} ${CCFLAGS} foo-gc-only.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log # check GC + RR-dylib -> error - ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib - ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log + ${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib -framework Foundation + ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib -framework Foundation 2> fail.log ${PASS_IFF} true diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/bar.m b/ld64/unit-tests/test-cases/objc-gc-checks/bar.m index 5c98709..f66df50 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/bar.m +++ b/ld64/unit-tests/test-cases/objc-gc-checks/bar.m @@ -1,5 +1,6 @@ +#include -@interface Bar { +@interface Bar : NSObject { int f; } - (void) doit; diff --git a/ld64/unit-tests/test-cases/objc-gc-checks/foo.m b/ld64/unit-tests/test-cases/objc-gc-checks/foo.m index e13367e..819049d 100644 --- a/ld64/unit-tests/test-cases/objc-gc-checks/foo.m +++ b/ld64/unit-tests/test-cases/objc-gc-checks/foo.m @@ -1,5 +1,6 @@ +#include -@interface Foo { +@interface Foo : NSObject { int f; } - (void) doit; diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m index 4837911..3ead0e6 100644 --- a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m +++ b/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m @@ -25,8 +25,10 @@ #include #include +#include -@interface Foo @end +@interface Foo : NSObject +@end @implementation Foo +(void)initialize { } +(void)foo { diff --git a/ld64/unit-tests/test-cases/objc-properties/test.m b/ld64/unit-tests/test-cases/objc-properties/test.m index 25ca8c8..4a568ae 100644 --- a/ld64/unit-tests/test-cases/objc-properties/test.m +++ b/ld64/unit-tests/test-cases/objc-properties/test.m @@ -24,7 +24,7 @@ #include -@interface Test +@interface Test : NSObject { BOOL one; NSString* two; diff --git a/ld64/unit-tests/test-cases/operator-new/main.cxx b/ld64/unit-tests/test-cases/operator-new/main.cxx index 7d1ef81..862e46e 100644 --- a/ld64/unit-tests/test-cases/operator-new/main.cxx +++ b/ld64/unit-tests/test-cases/operator-new/main.cxx @@ -48,7 +48,7 @@ public: }; void Foo::print() { - printf("%d\n", a); + printf("%d %d\n", a, b); } diff --git a/ld64/unit-tests/test-cases/branch-islands/Makefile b/ld64/unit-tests/test-cases/order_file-archive/Makefile similarity index 68% copy from ld64/unit-tests/test-cases/branch-islands/Makefile copy to ld64/unit-tests/test-cases/order_file-archive/Makefile index 8e1870b..eb77525 100644 --- a/ld64/unit-tests/test-cases/branch-islands/Makefile +++ b/ld64/unit-tests/test-cases/order_file-archive/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008-2009 Apple Inc. All rights reserved. +# Copyright (c) 2013 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,21 +23,21 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - # -# Simple test for branch islands +# Verify order files can pick file in archives # run: all - all: - # Verify that we fail if there is no valid place to insert branch islands. - ${CC} ${CCFLAGS} hello.c atomic_space.s extra.c -o hello ${ARCH_FLAGS} 2>&1 | grep "Unable to insert branch island. No insertion point available." | ${PASS_IFF_STDIN} - - ${CC} ${CCFLAGS} hello.c space.s extra.c -o hello ${ARCH_FLAGS} - ${PASS_IFF_GOOD_MACHO} hello + ${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 hello + rm -rf *.o libfoo.a main main.actual diff --git a/ld64/unit-tests/test-cases/order_file-archive/foo.c b/ld64/unit-tests/test-cases/order_file-archive/foo.c new file mode 100644 index 0000000..61be976 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/foo.c @@ -0,0 +1,4 @@ +void foo1() { } +void foo2() {} +void foo3() {} + diff --git a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m b/ld64/unit-tests/test-cases/order_file-archive/main.c similarity index 75% copy from ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m copy to ld64/unit-tests/test-cases/order_file-archive/main.c index 4837911..298aedc 100644 --- a/ld64/unit-tests/test-cases/objc-literal-pointers-strip/test.m +++ b/ld64/unit-tests/test-cases/order_file-archive/main.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,26 +21,13 @@ * * @APPLE_LICENSE_HEADER_END@ */ - - #include -#include -@interface Foo @end -@implementation Foo -+(void)initialize { } -+(void)foo { - fprintf(stderr, "GOOD\n"); - exit(0); -} -+(void)bar { - fprintf(stderr, "BAD\n"); - abort(); -} -@end -void PublicFunction(void) +extern void foo1(); + +int main() { - [Foo foo]; - [Foo bar]; + foo1(); + return 0; } diff --git a/ld64/unit-tests/test-cases/order_file-archive/main.expected b/ld64/unit-tests/test-cases/order_file-archive/main.expected new file mode 100644 index 0000000..31a7e2f --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/main.expected @@ -0,0 +1,4 @@ +_foo2 +_main +_foo3 +_foo1 diff --git a/ld64/unit-tests/test-cases/order_file-archive/main.order b/ld64/unit-tests/test-cases/order_file-archive/main.order new file mode 100644 index 0000000..195b1d7 --- /dev/null +++ b/ld64/unit-tests/test-cases/order_file-archive/main.order @@ -0,0 +1,5 @@ +_foo2 +_main +libfoo.a(foo.o):_foo3 +_foo1 + diff --git a/ld64/unit-tests/test-cases/re-export-symbol/Makefile b/ld64/unit-tests/test-cases/re-export-symbol/Makefile index 4da677c..2ad9e0c 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/Makefile +++ b/ld64/unit-tests/test-cases/re-export-symbol/Makefile @@ -44,10 +44,12 @@ all: ${DYLDINFO} -bind -lazy_bind main1 | grep _bar | grep libfoo | ${FAIL_IF_EMPTY} ${DYLDINFO} -bind -lazy_bind main1 | grep _bar_weak | grep libfoo | ${FAIL_IF_EMPTY} - # build library the re-exports _bar from base library as _mybar - ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp + # build library that re-exports _bar from base library as _mybar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp -DUSE_MY ${FAIL_IF_BAD_MACHO} libfoo2.dylib ${DYLDINFO} -export libfoo2.dylib | grep _mybar | grep 're-export' | grep _bar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -lazy_bind libfoo2.dylib | grep _bar | grep libbar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -bind libfoo2.dylib | grep _bar | grep libbar | ${FAIL_IF_EMPTY} # link against dylib and verify _mybar is marked as coming from libfoo ${CC} ${CCFLAGS} main2.c libfoo2.dylib -o main2 ${DYLDINFO} -bind -lazy_bind main2 | grep _mybar | grep libfoo2 | ${FAIL_IF_EMPTY} diff --git a/ld64/unit-tests/test-cases/re-export-symbol/foo.c b/ld64/unit-tests/test-cases/re-export-symbol/foo.c index 714540a..61981fe 100644 --- a/ld64/unit-tests/test-cases/re-export-symbol/foo.c +++ b/ld64/unit-tests/test-cases/re-export-symbol/foo.c @@ -1,4 +1,21 @@ + +#if USE_MY + extern int mybar(); +#else + extern int bar(); +#endif + int foo(void) { - return 1; +#if USE_MY + return mybar() + 1; +#else + return bar() + 1; +#endif } + +#if USE_MY + void* p = &mybar; +#else + void* p = &bar; +#endif diff --git a/ld64/unit-tests/test-cases/relocs-literals/Makefile b/ld64/unit-tests/test-cases/relocs-literals/Makefile index a9ca5ef..d62de34 100644 --- a/ld64/unit-tests/test-cases/relocs-literals/Makefile +++ b/ld64/unit-tests/test-cases/relocs-literals/Makefile @@ -35,7 +35,7 @@ include ${TESTROOT}/include/common.makefile run: all all: - ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o + ${CC} ${CCFLAGS} -Os $(MDYNAMIC_NO_PIC) test.c -c -o test.${ARCH}.o -Wno-array-bounds ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o diff --git a/ld64/unit-tests/test-cases/relocs-literals/test.c b/ld64/unit-tests/test-cases/relocs-literals/test.c index 2d199d0..5939ef4 100644 --- a/ld64/unit-tests/test-cases/relocs-literals/test.c +++ b/ld64/unit-tests/test-cases/relocs-literals/test.c @@ -48,7 +48,7 @@ long double getLongDouble() { return 3.0; } // rdar://problem/4732996 const char* stringFutz(int x) { - return "hello" + 0x1000 + x; + return &"hello"[0x1000 + x]; } -const char* usesAddend = "teststr" + 0x2000; +const char* usesAddend = &"teststr"[0x2000]; diff --git a/ld64/unit-tests/test-cases/relocs-literals2/Makefile b/ld64/unit-tests/test-cases/relocs-literals2/Makefile index 23e4a82..b1820f0 100644 --- a/ld64/unit-tests/test-cases/relocs-literals2/Makefile +++ b/ld64/unit-tests/test-cases/relocs-literals2/Makefile @@ -38,7 +38,7 @@ endif run: all all: - ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o + ${CC} ${CCFLAGS} -Os $(PIC) test.c -c -o test.${ARCH}.o -Wno-array-bounds ${FAIL_IF_ERROR} ${OBJECTDUMP} -no_content test.${ARCH}.o > test.${ARCH}.o.dump ${LD} -arch ${ARCH} -r -keep_private_externs test.${ARCH}.o -o test-r.${ARCH}.o diff --git a/ld64/unit-tests/test-cases/relocs-literals2/test.c b/ld64/unit-tests/test-cases/relocs-literals2/test.c index 2d199d0..5939ef4 100644 --- a/ld64/unit-tests/test-cases/relocs-literals2/test.c +++ b/ld64/unit-tests/test-cases/relocs-literals2/test.c @@ -48,7 +48,7 @@ long double getLongDouble() { return 3.0; } // rdar://problem/4732996 const char* stringFutz(int x) { - return "hello" + 0x1000 + x; + return &"hello"[0x1000 + x]; } -const char* usesAddend = "teststr" + 0x2000; +const char* usesAddend = &"teststr"[0x2000]; diff --git a/ld64/unit-tests/test-cases/relocs-objc/test.m b/ld64/unit-tests/test-cases/relocs-objc/test.m index 1ca2157..2b2bf9c 100644 --- a/ld64/unit-tests/test-cases/relocs-objc/test.m +++ b/ld64/unit-tests/test-cases/relocs-objc/test.m @@ -48,7 +48,7 @@ -@interface Base +@interface Base : NSObject @end -- 2.11.4.GIT