From eaf282aaf65b222563e6b3db98e12d720fb161bf Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 25 Aug 2015 17:19:19 +0000 Subject: [PATCH] ld64-253.3.tar.gz --- doc/man/man1/ld.1 | 32 +- ld64.xcodeproj/project.pbxproj | 36 +- src/abstraction/MachOFileAbstraction.hpp | 43 +- src/create_configure | 22 +- src/ld/Bitcode.hpp | 87 ++ src/ld/HeaderAndLoadCommands.hpp | 126 +- src/ld/InputFiles.cpp | 59 +- src/ld/LinkEdit.hpp | 205 ++-- src/ld/LinkEditClassic.hpp | 14 +- src/ld/Options.cpp | 808 ++++++++++--- src/ld/Options.h | 95 +- src/ld/OutputFile.cpp | 354 +++++- src/ld/OutputFile.h | 20 +- src/ld/Resolver.cpp | 191 ++- src/ld/Resolver.h | 1 + src/ld/SymbolTable.cpp | 9 + src/ld/SymbolTable.h | 1 + src/ld/code-sign-blobs/memutils.h | 7 +- src/ld/ld.cpp | 130 +- src/ld/ld.hpp | 56 +- src/ld/lto_file.hpp | 642 ---------- src/ld/parsers/lto_file.cpp | 181 ++- src/ld/parsers/lto_file.h | 5 + src/ld/parsers/macho_dylib_file.cpp | 270 +++-- src/ld/parsers/macho_relocatable_file.cpp | 366 +++++- src/ld/parsers/macho_relocatable_file.h | 11 +- src/ld/parsers/opaque_section_file.cpp | 3 +- src/ld/parsers/textstub_dylib_file.cpp | 1072 +++++++++++++++++ src/ld/parsers/textstub_dylib_file.hpp | 43 + src/ld/passes/bitcode_bundle.cpp | 809 +++++++++++++ src/ld/passes/bitcode_bundle.h | 43 + src/ld/passes/branch_island.cpp | 5 +- src/ld/passes/branch_shim.cpp | 2 + src/ld/passes/dylibs.cpp | 1 + src/ld/passes/got.cpp | 25 +- src/ld/passes/huge.cpp | 2 + src/ld/passes/objc.cpp | 13 +- src/ld/passes/order.cpp | 14 +- src/ld/passes/stubs/stub_arm.hpp | 30 +- src/ld/passes/stubs/stub_arm64.hpp | 22 +- src/ld/passes/stubs/stubs.cpp | 13 +- src/ld/passes/tlvp.cpp | 7 +- src/other/ObjectDump.cpp | 4 +- src/other/dyldinfo.cpp | 344 ++++-- unit-tests/include/common.makefile | 13 +- .../dead_strip-live-if-ref-live/Makefile | 48 + .../dead_strip-live-if-ref-live/main.c | 29 + unit-tests/test-cases/tlv-basic/get.s | 113 ++ unit-tests/test-cases/tlv-basic/main.c | 19 +- 49 files changed, 5105 insertions(+), 1340 deletions(-) create mode 100644 src/ld/Bitcode.hpp delete mode 100644 src/ld/lto_file.hpp create mode 100644 src/ld/parsers/textstub_dylib_file.cpp create mode 100644 src/ld/parsers/textstub_dylib_file.hpp create mode 100644 src/ld/passes/bitcode_bundle.cpp create mode 100644 src/ld/passes/bitcode_bundle.h create mode 100644 unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile create mode 100644 unit-tests/test-cases/dead_strip-live-if-ref-live/main.c diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index df4b5a4..1c118b8 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -117,6 +117,10 @@ Produce a mach-o dylinker that has file type MH_DYLINKER. Only used when buildi The default. Implied by -dylib, -bundle, or -execute .It Fl static Produces a mach-o file that does not use the dyld. Only used building the kernel. +.It Fl preload +Produces a mach-o file in which the mach_header, load commands, and symbol table are +not in any segment. This output type is used for firmware or embedded development +where the segments are copied out of the mach-o into ROM/Flash. .It Fl arch Ar arch_name Specifies which architecture (e.g. ppc, ppc64, i386, x86_64) the output file should be. .It Fl o Ar path @@ -480,6 +484,7 @@ Logs a chain of references to .Ar symbol_name . Only applicable with -dead_strip . It can help debug why something that you think should be dead strip removed is not removed. +See -exported_symbols_list for syntax and use of wildcards. .It Fl print_statistics Logs information about the amount of memory and time the linker used. .It Fl t @@ -515,10 +520,31 @@ contains a list of non-global symbol names that should be remain in the output f symbol names will be removed from the output file's symbol table. See -exported_symbols_list for syntax and use of wildcards. .El +.Ss Options for Bitcode build flow +.Bl -tag +.It Fl bitcode_bundle +Generates an embedded bitcode bundle in the output binary. The bitcode bundle is embedded in __LLVM, __bundle section. +This option requires all the object files, static libraries and user frameworks/dylibs contain bitcode. +Note: not all the linker options are supported to use together with -bitcode_bundle. +.It Fl bitcode_hide_symbol +Specifies this option together with -bitcode_bundle to hide all non-exported symbols from output bitcode bundle. +The hide symbol process might not be reversible. To obtain a reverse mapping file to recover all the symbols, use +-bitcode_symbol_map option. +.It Fl bitcode_symbol_map Ar path +Specifies the output for bitcode symbol reverse mapping (.bcsymbolmap). If +.Ar path +is an existing directory, UUID.bcsymbolmap will be written to that directory. +Otherwise, the reverse map will be written to a file at +.Ar path . +.El .Ss Rarely used Options .Bl -tag .It Fl v Prints the version of the linker. +.It Fl dirty_data_list Ar filename +Specifies a file containing the names of data symbols likely to be dirtied. +If the linker is creating a __DATA_DIRTY segment, those symbols will be moved +to that segment. .It Fl move_to_rw_segment Ar segment_name Ar filename Moves data symbols to another segment. The command line option specifies the target segment name and a path to a file containing a list of symbols to move. @@ -738,6 +764,10 @@ info in the temporary object file. During development, this option can be used to space out all global variables so each is on a separate page. This is useful when analyzing dirty and resident pages. The information can then be used to create an order file to cluster commonly used/dirty globals onto the same page(s). +.It Fl not_for_dyld_shared_cache +Normally, the linker will add extra info to dylibs with -install_name starting with /usr/lib or +/System/Library/ that allows the dylib to be placed into the dyld shared cache. Adding this option +tells the linker to not add that extra info. .El .Ss Obsolete Options .Bl -tag @@ -749,8 +779,6 @@ Object files (MH_OBJECT) with a LINKEDIT segment are no longer supported. This o This is the default. This option is obsolete. .It Fl fvmlib Fixed VM shared libraries (MH_FVMLIB) are no longer supported. This option is obsolete. -.It Fl preload -Preload executables (MH_PRELOAD) are no longer supported. .It Fl sectobjectsymbols Ar segname Ar sectname Adding a local label at a section start is no longer supported. This option is obsolete. .It Fl nofixprebinding diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index b4d1749..1c5e622 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + B028FCF21A9E7C3F00E3584B /* bitcode_bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */; }; B3B672421406D42800A376BB /* Snapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3B672411406D42800A376BB /* Snapshot.cpp */; }; F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9023C3F06D5A254001BBF46 /* ld.cpp */; }; F933E3D9092E855B0083EAC8 /* ObjectDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EED706D5AD240041D381 /* ObjectDump.cpp */; }; @@ -82,6 +83,7 @@ F9EA7584097882F3008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EA75BC09788857008B4F1D /* debugline.c in Sources */ = {isa = PBXBuildFile; fileRef = F9EA7582097882F3008B4F1D /* debugline.c */; }; F9EC78060A2F8674002A3E39 /* rebase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9EC78050A2F8674002A3E39 /* rebase.cpp */; }; + FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -241,6 +243,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bitcode_bundle.h; sourceTree = ""; }; + B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitcode_bundle.cpp; sourceTree = ""; }; + B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Bitcode.hpp; path = src/ld/Bitcode.hpp; sourceTree = ""; }; B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; B3B672441406D44300A376BB /* Snapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = Snapshot.h; path = src/ld/Snapshot.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; @@ -333,6 +338,8 @@ F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F9EC77EE0A2F85F6002A3E39 /* rebase */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rebase; sourceTree = BUILT_PRODUCTS_DIR; }; F9EC78050A2F8674002A3E39 /* rebase.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = rebase.cpp; path = src/other/rebase.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; + FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = textstub_dylib_file.cpp; sourceTree = ""; usesTabs = 1; }; + FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = textstub_dylib_file.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -421,6 +428,8 @@ F9AA650B1051BD2B003E3539 /* passes */ = { isa = PBXGroup; children = ( + B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */, + B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */, F984A38010BB4B0D009E9878 /* branch_island.cpp */, F984A38110BB4B0D009E9878 /* branch_island.h */, F9AA44DA1294885F00CB8390 /* branch_shim.cpp */, @@ -466,6 +475,8 @@ F9AA65861051E750003E3539 /* parsers */ = { isa = PBXGroup; children = ( + FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */, + FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */, F91B7B0218987D5F0099486F /* libunwind */, F9AA6784105700C2003E3539 /* opaque_section_file.cpp */, F9AA6785105700C2003E3539 /* opaque_section_file.h */, @@ -525,6 +536,7 @@ F9AA650B1051BD2B003E3539 /* passes */, F9AA65861051E750003E3539 /* parsers */, F933DC37092A82480083EAC8 /* Architectures.hpp */, + B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */, F9EA7582097882F3008B4F1D /* debugline.c */, F9EA7583097882F3008B4F1D /* debugline.h */, B3B672411406D42800A376BB /* Snapshot.cpp */, @@ -772,7 +784,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n"; + shellScript = "\nif [ -n \"${DT_TOOLCHAIN_DIR}\" ]\nthen\n\tmkdir -p \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\n\tmv ${DSTROOT}/usr \"${DSTROOT}/${DT_VARIANT}/${DT_TOOLCHAIN_DIR}\"\nelse\n\tif [ -n \"${RC_PURPLE}\" ]\n\tthen\n\t\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\t\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\n\telse\n\t\tmkdir -p ${DSTROOT}/Developer/usr/bin\n\t\tcp ${DSTROOT}/usr/bin/ld ${DSTROOT}/Developer/usr/bin\n\tfi\nfi\n\n"; showEnvVarsInLog = 0; }; F9CCF765144CE244007CB524 /* make configure.h */ = { @@ -894,6 +906,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FA95D6141AB25CF400395811 /* textstub_dylib_file.cpp in Sources */, F9C0D4BD06DD28D2001C7193 /* Options.cpp in Sources */, F9023C4E06D5A272001BBF46 /* ld.cpp in Sources */, F9AA65891051E750003E3539 /* macho_relocatable_file.cpp in Sources */, @@ -918,6 +931,7 @@ F93CB248116E69EB003233B8 /* tlvp.cpp in Sources */, F9AA44DC1294885F00CB8390 /* branch_shim.cpp in Sources */, B3B672421406D42800A376BB /* Snapshot.cpp in Sources */, + B028FCF21A9E7C3F00E3584B /* bitcode_bundle.cpp in Sources */, F9CC24191461FB4300A92174 /* blob.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1037,6 +1051,8 @@ F933D91C09291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1080,13 +1096,16 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../lib/"; LINKER_DISPLAYS_MANGLED_NAMES = NO; MACOSX_DEPLOYMENT_TARGET = ""; + ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CPLUSPLUSFLAGS)", ); OTHER_LDFLAGS = ( "-stdlib=libc++", + "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", + "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1100,6 +1119,8 @@ F933D91D09291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1151,7 +1172,9 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", + "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", + "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1168,6 +1191,7 @@ F933D92009291AC90083EAC8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1186,8 +1210,10 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/src/ld", "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; + ONLY_ACTIVE_ARCH = NO; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1211,6 +1237,7 @@ F933D92109291AC90083EAC8 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1221,6 +1248,7 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/src/ld", "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; OTHER_CPLUSPLUSFLAGS = ( @@ -1299,6 +1327,8 @@ F9849FFA10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1349,7 +1379,9 @@ ); OTHER_LDFLAGS = ( "-stdlib=libc++", + "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", + "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", ); PREBINDING = NO; @@ -1413,6 +1445,7 @@ F9849FFD10B5DE8E009E9878 /* Release-assert */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1424,6 +1457,7 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/src/ld", "$(DEVELOPER_DIR)/usr/local/include", + "$(DT_TOOLCHAIN_DIR)/usr/local/include", ); INSTALL_PATH = "$(HOME)/bin"; OTHER_CPLUSPLUSFLAGS = ( diff --git a/src/abstraction/MachOFileAbstraction.hpp b/src/abstraction/MachOFileAbstraction.hpp index ae61d7c..cfedc81 100644 --- a/src/abstraction/MachOFileAbstraction.hpp +++ b/src/abstraction/MachOFileAbstraction.hpp @@ -426,11 +426,18 @@ #define LOH_ARM64_ADRP_LDR_GOT 8 #endif +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 #endif - #ifndef CPU_SUBTYPE_ARM_V8 #define CPU_SUBTYPE_ARM_V8 ((cpu_subtype_t) 13) #endif @@ -472,6 +479,40 @@ #define UNWIND_ARM_DWARF_SECTION_OFFSET 0x00FFFFFF + +// ( (delta-uleb128)+ )+ +#define DYLD_CACHE_ADJ_V1_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V1_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V1_ADRP 0x03 +#define DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT 0x10 // thru 0x1F +#define DYLD_CACHE_ADJ_V1_ARM_MOVT 0x20 // thru 0x2F + + +// Whole :== FromToSection+ +// FromToSection :== ToOffset+ +// ToOffset :== FromOffset+ +// FromOffset :== +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F + +#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 +#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 +#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 +#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 +#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 +#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 +#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 +#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A +#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B +#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C + + + +// kind target-address fixup-addr [adj] + + + struct ArchInfo { const char* archName; cpu_type_t cpuType; diff --git a/src/create_configure b/src/create_configure index f991231..9ce654d 100755 --- a/src/create_configure +++ b/src/create_configure @@ -25,8 +25,24 @@ do fi done -echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h - +if [ -n "${RC_HIDE_TIDE}" ]; then + echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h + echo "#define SUPPORT_APPLE_TV 0" >> ${DERIVED_FILE_DIR}/configure.h +else + if [ -n "${DT_VARIANT}" -a "${DT_VARIANT}" != "PONDEROSA" ]; then + echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS}\"" >> ${DERIVED_FILE_DIR}/configure.h + echo "#define SUPPORT_APPLE_TV 0" >> ${DERIVED_FILE_DIR}/configure.h + else + echo "#define ALL_SUPPORTED_ARCHS \"${RC_SUPPORTED_ARCHS} (tvOS)\"" >> ${DERIVED_FILE_DIR}/configure.h + echo "#define SUPPORT_APPLE_TV 1" >> ${DERIVED_FILE_DIR}/configure.h + fi +fi - +if [ -f "${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" ]; then + echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" > ${DERIVED_FILE_DIR}/linkExtras + echo "#define DEMANGLE_SWIFT 1" >> ${DERIVED_FILE_DIR}/configure.h +else + echo "" > ${DERIVED_FILE_DIR}/linkExtras +fi +echo "#define BITCODE_XAR_VERSION \"1.0\"" >> ${DERIVED_FILE_DIR}/configure.h diff --git a/src/ld/Bitcode.hpp b/src/ld/Bitcode.hpp new file mode 100644 index 0000000..68822d2 --- /dev/null +++ b/src/ld/Bitcode.hpp @@ -0,0 +1,87 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __BITCODE_HPP__ +#define __BITCODE_HPP__ + +#include + +namespace ld { + +class Bitcode { +public: + Bitcode(const uint8_t* content, uint32_t size) : _content(content), _size(size) { } + + virtual bool isMarker() const { return _size <= 1 ; } + virtual const uint8_t* getContent() const { return _content; } + virtual uint32_t getSize() const { return _size; } +private: + const uint8_t* _content; + uint32_t _size; +}; + +class LLVMBitcode : public Bitcode { +public: + LLVMBitcode(const uint8_t* content, uint32_t size, const uint8_t* cmd, uint32_t cmdSize) : + Bitcode(content, size), _cmdline(cmd), _cmdSize(cmdSize) { } + + virtual const uint8_t* getCmdline() const { return _cmdline; } + virtual uint32_t getCmdSize() const { return _cmdSize; } + virtual const char* getBitcodeName() const { return "llvm"; } +private: + const uint8_t* _cmdline; + uint32_t _cmdSize; +}; + +class ClangBitcode : public LLVMBitcode { +public: + ClangBitcode(const uint8_t* content, uint32_t size, const uint8_t* cmd, uint32_t cmdSize) : + LLVMBitcode(content, size, cmd, cmdSize) { } + virtual const char* getBitcodeName() const override { return "clang"; } +}; + +class SwiftBitcode : public LLVMBitcode { +public: + SwiftBitcode(const uint8_t* content, uint32_t size, const uint8_t* cmd, uint32_t cmdSize) : + LLVMBitcode(content, size, cmd, cmdSize) { } + virtual const char* getBitcodeName() const override { return "swift"; } +}; + +class AsmBitcode : public Bitcode { +public: + AsmBitcode(const uint8_t* content, uint32_t size) : Bitcode(content, size) { } + + virtual bool isMarker() const override { return false; } +}; + +class BundleBitcode : public Bitcode { +public: + BundleBitcode(const uint8_t* content, uint32_t size) : + Bitcode(content, size) { } +}; + +} + + +#endif /* defined(__BITCODE_HPP__) */ diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index 3629af5..b47acff 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -49,6 +49,9 @@ public: virtual void setUUID(const uint8_t digest[16]) = 0; virtual void recopyUUIDCommand() = 0; + virtual const uint8_t* getUUID() const = 0; + virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd, + uint64_t& sectOffset, uint64_t& sectEnd) const = 0; }; template @@ -68,6 +71,9 @@ public: // overrides of HeaderAndLoadCommandsAbtract virtual void setUUID(const uint8_t digest[16]) { memcpy(_uuid, digest, 16); } virtual void recopyUUIDCommand(); + virtual const uint8_t* getUUID() const { return &_uuid[0]; } + virtual bool bitcodeBundleCommand(uint64_t& cmdOffset, uint64_t& cmdEnd, + uint64_t& sectOffset, uint64_t& sectEnd) const; private: typedef typename A::P P; @@ -107,7 +113,6 @@ private: uint8_t* copySubUmbrellaLoadCommand(uint8_t* p, const char* name) const; uint8_t* copyFunctionStartsLoadCommand(uint8_t* p) const; uint8_t* copyDataInCodeLoadCommand(uint8_t* p) const; - uint8_t* copyDependentDRLoadCommand(uint8_t* p) const; uint8_t* copyDyldEnvLoadCommand(uint8_t* p, const char* env) const; uint8_t* copyLinkerOptionsLoadCommand(uint8_t* p, const std::vector&) const; uint8_t* copyOptimizationHintsLoadCommand(uint8_t* p) const; @@ -137,7 +142,6 @@ private: bool _hasFunctionStartsLoadCommand; bool _hasDataInCodeLoadCommand; bool _hasSourceVersionLoadCommand; - bool _hasDependentDRInfo; bool _hasOptimizationHints; uint32_t _dylibLoadCommmandsCount; uint32_t _allowableClientLoadCommmandsCount; @@ -223,11 +227,12 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: } _hasRPathLoadCommands = (_options.rpaths().size() != 0); _hasSubFrameworkLoadCommand = (_options.umbrellaName() != NULL); - _hasVersionLoadCommand = _options.addVersionLoadCommand(); + _hasVersionLoadCommand = _options.addVersionLoadCommand() || + (!state.objectFileFoundWithNoVersion && (_options.outputKind() == Options::kObjectFile) + && ((_options.platform() != Options::kPlatformUnknown) || (state.derivedPlatformLoadCommand != 0)) ); _hasFunctionStartsLoadCommand = _options.addFunctionStarts(); _hasDataInCodeLoadCommand = _options.addDataInCodeInfo(); _hasSourceVersionLoadCommand = _options.needsSourceVersionLoadCommand(); - _hasDependentDRInfo = _options.needsDependentDRInfo(); _dylibLoadCommmandsCount = _writer.dylibCount(); _allowableClientLoadCommmandsCount = _options.allowableClients().size(); _dyldEnvironExrasCount = _options.dyldEnvironExtras().size(); @@ -326,6 +331,31 @@ unsigned int HeaderAndLoadCommandsAtom::segmentCount() const return count; } +template +bool HeaderAndLoadCommandsAtom::bitcodeBundleCommand(uint64_t &cmdOffset, uint64_t &cmdEnd, + uint64_t §Offset, uint64_t §End) const +{ + if ( _options.outputKind() == Options::kObjectFile ) { + return false; + } + cmdOffset = sizeof(macho_header

); + const char* lastSegName = ""; + for (std::vector::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) { + if ( strcmp(lastSegName, (*it)->segmentName()) != 0 ) { + lastSegName = (*it)->segmentName(); + cmdOffset += sizeof(macho_segment_command

); + } + if ( strcmp((*it)->segmentName(), "__LLVM") == 0 && strcmp((*it)->sectionName(), "__bundle") == 0 ) { + sectOffset = (*it)->fileOffset; + sectEnd = (*(it + 1))->fileOffset; + cmdEnd = cmdOffset + sizeof(macho_section

); + return true; + } + if ( ! (*it)->isSectionHidden() ) + cmdOffset += sizeof(macho_section

); + } + return false; +} template uint64_t HeaderAndLoadCommandsAtom::size() const @@ -427,9 +457,6 @@ uint64_t HeaderAndLoadCommandsAtom::size() const } } - if ( _hasDependentDRInfo ) - sz += sizeof(macho_linkedit_data_command

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

); @@ -507,9 +534,6 @@ uint32_t HeaderAndLoadCommandsAtom::commandsCount() const } } - if ( _hasDependentDRInfo ) - ++count; - if ( _hasOptimizationHints ) ++count; @@ -692,6 +716,7 @@ struct SegInfo { SegInfo(const char* n, const Options&); const char* segName; uint32_t nonHiddenSectionCount; + uint32_t nonSectCreateSections; uint32_t maxProt; uint32_t initProt; std::vector sections; @@ -699,7 +724,7 @@ struct SegInfo { SegInfo::SegInfo(const char* n, const Options& opts) - : segName(n), nonHiddenSectionCount(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n)) + : segName(n), nonHiddenSectionCount(0), nonSectCreateSections(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n)) { } @@ -722,6 +747,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* return S_REGULAR | S_ATTR_NO_DEAD_STRIP; else if ( (strncmp(sect->sectionName(), "__objc_nlcatlist", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) return S_REGULAR | S_ATTR_NO_DEAD_STRIP; + else if ( (_options.outputKind() == Options::kObjectFile) && !sect->atoms.empty() && sect->atoms.front()->dontDeadStripIfReferencesLive() ) + return S_REGULAR | S_ATTR_LIVE_SUPPORT; else return S_REGULAR; case ld::Section::typeCode: @@ -841,6 +868,8 @@ uint32_t HeaderAndLoadCommandsAtom::sectionFlags(ld::Internal::FinalSection* return S_REGULAR; case ld::Section::typeDebug: return S_REGULAR | S_ATTR_DEBUG; + case ld::Section::typeSectCreate: + return S_REGULAR; } return S_REGULAR; } @@ -885,6 +914,9 @@ uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const } if ( ! sect->isSectionHidden() ) segs.back().nonHiddenSectionCount++; + if ( sect->type() != ld::Section::typeSectCreate ) + segs.back().nonSectCreateSections++; + segs.back().sections.push_back(sect); } // write out segment load commands for each section with trailing sections @@ -921,7 +953,8 @@ uint8_t* HeaderAndLoadCommandsAtom::copySegmentLoadCommands(uint8_t* p) const segCmd->set_maxprot(si.maxProt); segCmd->set_initprot(si.initProt); segCmd->set_nsects(si.nonHiddenSectionCount); - segCmd->set_flags(0); + segCmd->set_flags(si.nonSectCreateSections ? 0 : SG_NORELOC); // FIXME, really should check all References + p += sizeof(macho_segment_command

); macho_section

* msect = (macho_section

*)p; for (std::vector::iterator sit = si.sections.begin(); sit != si.sections.end(); ++sit) { @@ -1104,20 +1137,40 @@ template uint8_t* HeaderAndLoadCommandsAtom::copyVersionLoadCommand(uint8_t* p) const { macho_version_min_command

* cmd = (macho_version_min_command

*)p; - ld::MacVersionMin macVersion = _options.macosxVersionMin(); - ld::IOSVersionMin iOSVersion = _options.iOSVersionMin(); - assert( (macVersion != ld::macVersionUnset) || (iOSVersion != ld::iOSVersionUnset) ); - if ( macVersion != ld::macVersionUnset ) { - cmd->set_cmd(LC_VERSION_MIN_MACOSX); - cmd->set_cmdsize(sizeof(macho_version_min_command

)); - cmd->set_version((uint32_t)macVersion); - cmd->set_sdk(_options.sdkVersion()); - } - else { - cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); - cmd->set_cmdsize(sizeof(macho_version_min_command

)); - cmd->set_version((uint32_t)iOSVersion); - cmd->set_sdk(_options.sdkVersion()); + switch (_options.platform()) { + case Options::kPlatformUnknown: + assert(_state.derivedPlatformLoadCommand != 0 && "unknown platform"); + cmd->set_cmd(_state.derivedPlatformLoadCommand); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(0); + break; + case Options::kPlatformOSX: + cmd->set_cmd(LC_VERSION_MIN_MACOSX); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; + case Options::kPlatformiOS: + cmd->set_cmd(LC_VERSION_MIN_IPHONEOS); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; + case Options::kPlatformWatchOS: + cmd->set_cmd(LC_VERSION_MIN_WATCHOS); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + cmd->set_cmd(LC_VERSION_MIN_TVOS); + cmd->set_cmdsize(sizeof(macho_version_min_command

)); + cmd->set_version(_state.minOSVersion); + cmd->set_sdk(_options.sdkVersion()); + break; +#endif } return p + sizeof(macho_version_min_command

); } @@ -1158,7 +1211,7 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) cons template <> uint32_t HeaderAndLoadCommandsAtom::threadLoadCommandSize() const { - return this->alignedSize(16 + x86_THREAD_STATE64_COUNT * 4); + return this->alignedSize(16 + 42*4); // base size + x86_THREAD_STATE64_COUNT * 4 } template <> @@ -1169,8 +1222,8 @@ uint8_t* HeaderAndLoadCommandsAtom::copyThreadsLoadCommand(uint8_t* p) c macho_thread_command

* cmd = (macho_thread_command

*)p; cmd->set_cmd(LC_UNIXTHREAD); cmd->set_cmdsize(threadLoadCommandSize()); - cmd->set_flavor(x86_THREAD_STATE64); - cmd->set_count(x86_THREAD_STATE64_COUNT); + cmd->set_flavor(4); // x86_THREAD_STATE64 + cmd->set_count(42); // x86_THREAD_STATE64_COUNT cmd->set_thread_register(16, start); // rip if ( _options.hasCustomStack() ) cmd->set_thread_register(7, _options.customStackAddr()); // r1 @@ -1411,18 +1464,6 @@ uint8_t* HeaderAndLoadCommandsAtom::copyLinkerOptionsLoadCommand(uint8_t* p, } -template -uint8_t* HeaderAndLoadCommandsAtom::copyDependentDRLoadCommand(uint8_t* p) const -{ - macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

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

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

); -} - - template uint8_t* HeaderAndLoadCommandsAtom::copyOptimizationHintsLoadCommand(uint8_t* p) const @@ -1546,9 +1587,6 @@ void HeaderAndLoadCommandsAtom::copyRawContent(uint8_t buffer[]) const } } - if ( _hasDependentDRInfo ) - p = this->copyDependentDRLoadCommand(p); - if ( _hasOptimizationHints ) p = this->copyOptimizationHintsLoadCommand(p); diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index 30c0875..9eadc2e 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -58,6 +58,7 @@ #include "InputFiles.h" #include "macho_relocatable_file.h" #include "macho_dylib_file.h" +#include "textstub_dylib_file.hpp" #include "archive_file.h" #include "lto_file.h" #include "opaque_section_file.h" @@ -294,7 +295,17 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib objOpts.neverConvertDwarf = !_options.needsUnwindInfoSection(); objOpts.verboseOptimizationHints = _options.verboseOptimizationHints(); objOpts.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); + objOpts.simulator = _options.targetIOSSimulator(); + objOpts.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable)); objOpts.subType = _options.subArchitecture(); + objOpts.platform = _options.platform(); + objOpts.minOSVersion = _options.minOSversion(); + // workaround for strip -S + // when ld -r has single input file, set the srcKind to kSourceSingle so __LLVM segment will be kept + if (_options.outputKind() == Options::kObjectFile && _options.getInputFiles().size() == 1) + objOpts.srcKind = ld::relocatable::File::kSourceSingle; + else + objOpts.srcKind = ld::relocatable::File::kSourceObj; ld::relocatable::File* objResult = mach_o::relocatable::parse(p, len, info.path, info.modTime, info.ordinal, objOpts); if ( objResult != NULL ) { OSAtomicAdd64(len, &_totalObjectSize); @@ -310,7 +321,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib return objResult; } - // see if it is a dynamic library + // see if it is a dynamic library (or text-based dynamic library) ld::dylib::File* dylibResult; bool dylibsNotAllowed = false; switch ( _options.outputKind() ) { @@ -321,6 +332,10 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib if ( dylibResult != NULL ) { return dylibResult; } + dylibResult = textstub::dylib::parse(p, len, info.path, info.modTime, _options, info.ordinal, info.options.fBundleLoader, indirectDylib); + if ( dylibResult != NULL ) { + return dylibResult; + } break; case Options::kStaticExecutable: case Options::kDyld: @@ -331,7 +346,6 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib break; } - // see if it is a static library ::archive::ParserOptions archOpts; archOpts.objOpts = objOpts; @@ -341,6 +355,12 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib archOpts.objcABI2 = _options.objCABIVersion2POverride(); archOpts.verboseLoad = _options.whyLoad(); archOpts.logAllFiles = _options.logAllFiles(); + // Set ObjSource Kind, libclang_rt is compiler static library + const char* libName = strrchr(info.path, '/'); + if ( (libName != NULL) && (strncmp(libName, "/libclang_rt", 12) == 0) ) + archOpts.objOpts.srcKind = ld::relocatable::File::kSourceCompilerArchive; + else + archOpts.objOpts.srcKind = ld::relocatable::File::kSourceArchive; ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, info.ordinal, archOpts); if ( archiveResult != NULL ) { OSAtomicAdd64(len, &_totalArchiveSize); @@ -587,6 +607,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan ld::dylib::File* dylibReader = dynamic_cast(reader); if ( dylibReader != NULL ) { if ( ! dylibReader->installPathVersionSpecific() ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -613,6 +634,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan ld::dylib::File* dylibReader = dynamic_cast(reader); ld::archive::File* archiveReader = dynamic_cast(reader); if ( dylibReader != NULL ) { + dylibReader->forEachAtom(handler); dylibReader->setImplicitlyLinked(); this->addDylib(dylibReader, info); } @@ -775,20 +797,27 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) _inferredArch = true; // scan all input files, looking for a thin .o file. // the first one found is presumably the architecture to link - uint8_t buffer[sizeof(mach_header_64)]; + uint8_t buffer[4096]; const std::vector& files = opts.getInputFiles(); for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) { int fd = ::open(it->path, O_RDONLY, 0); if ( fd != -1 ) { - ssize_t amount = read(fd, buffer, sizeof(buffer)); - ::close(fd); - if ( amount >= (ssize_t)sizeof(buffer) ) { - cpu_type_t type; - cpu_subtype_t subtype; - if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype) ) { - opts.setArchitecture(type, subtype); - *archName = opts.architectureName(); - return; + struct stat stat_buf; + if ( fstat(fd, &stat_buf) != -1) { + ssize_t readAmount = stat_buf.st_size; + if ( 4096 < readAmount ) + readAmount = 4096; + ssize_t amount = read(fd, buffer, readAmount); + ::close(fd); + if ( amount >= readAmount ) { + cpu_type_t type; + cpu_subtype_t subtype; + Options::Platform platform; + if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype, &platform) ) { + opts.setArchitecture(type, subtype, platform); + *archName = opts.architectureName(); + return; + } } } } @@ -797,11 +826,11 @@ void InputFiles::inferArchitecture(Options& opts, const char** archName) // no thin .o files found, so default to same architecture this tool was built as warning("-arch not specified"); #if __i386__ - opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL); + opts.setArchitecture(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, Options::kPlatformOSX); #elif __x86_64__ - opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL); + opts.setArchitecture(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, Options::kPlatformOSX); #elif __arm__ - opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6); + opts.setArchitecture(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6, Options::kPlatformOSX); #else #error unknown default architecture #endif diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp index 3420626..c4421ef 100644 --- a/src/ld/LinkEdit.hpp +++ b/src/ld/LinkEdit.hpp @@ -32,6 +32,7 @@ #include #include +#include #include "Options.h" #include "ld.hpp" @@ -81,6 +82,17 @@ public: while( more ); } + void append_delta_encoded_uleb128_run(uint64_t start, const std::vector& locations) { + uint64_t lastAddr = start; + for(std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { + uint64_t nextAddr = *it; + uint64_t delta = nextAddr - lastAddr; + assert(delta != 0); + append_uleb128(delta); + lastAddr = nextAddr; + } + } + void append_string(const char* str) { for (const char* s = str; *s != '\0'; ++s) _data.push_back(*s); @@ -219,6 +231,8 @@ void RebaseInfoAtom::encode() const } mid.push_back(rebase_tmp(REBASE_OPCODE_DO_REBASE_ULEB_TIMES, 1)); address += sizeof(pint_t); + if ( address >= curSegEnd ) + address = 0; } mid.push_back(rebase_tmp(REBASE_OPCODE_DONE, 0)); @@ -969,6 +983,7 @@ void ExportInfoAtom::encode() const std::vector& exports = this->_writer._exportedAtoms; uint64_t imageBaseAddress = this->_writer.headerAndLoadCommandsSection->address; std::vector entries; + unsigned int padding = 0; entries.reserve(exports.size()); for (std::vector::const_iterator it = exports.begin(); it != exports.end(); ++it) { const ld::Atom* atom = *it; @@ -1035,6 +1050,13 @@ void ExportInfoAtom::encode() const entry.importName = NULL; entries.push_back(entry); } + + if (_options.sharedRegionEligible() && strncmp(atom->section().segmentName(), "__DATA", 6) == 0) { + // Maximum address is 64bit which is 10 bytes as a uleb128. Minimum is 1 byte + // Pad the section out so we can deal with addresses getting larger when __DATA segment + // is moved before __TEXT in dyld shared cache. + padding += 9; + } } // sort vector by -exported_symbols_order, and any others by address @@ -1043,6 +1065,10 @@ void ExportInfoAtom::encode() const // create trie mach_o::trie::makeTrie(entries, this->_encodedData.bytes()); + //Add additional data padding for the unoptimized shared cache + for (unsigned int i = 0; i < padding; ++i) + this->_encodedData.append_byte(0); + // align to pointer size this->_encodedData.pad_to_size(sizeof(pint_t)); @@ -1051,10 +1077,10 @@ void ExportInfoAtom::encode() const template -class SplitSegInfoAtom : public LinkEditAtom +class SplitSegInfoV1Atom : public LinkEditAtom { public: - SplitSegInfoAtom(const Options& opts, ld::Internal& state, OutputFile& writer) + SplitSegInfoV1Atom(const Options& opts, ld::Internal& state, OutputFile& writer) : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } // overrides of ld::Atom @@ -1083,10 +1109,10 @@ private: }; template -ld::Section SplitSegInfoAtom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); +ld::Section SplitSegInfoV1Atom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreX86PCRel32: @@ -1116,7 +1142,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind } template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreLittleEndian32: @@ -1132,7 +1158,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki } template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreLittleEndian32: @@ -1160,7 +1186,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind ki #if SUPPORT_ARCH_arm64 template <> -void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const +void SplitSegInfoV1Atom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const { switch (kind) { case ld::Fixup::kindStoreARM64Page21: @@ -1188,7 +1214,7 @@ void SplitSegInfoAtom::addSplitSegInfo(uint64_t address, ld::Fixup::Kind #endif template -void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& locations) const +void SplitSegInfoV1Atom::uleb128EncodeAddresses(const std::vector& locations) const { pint_t addr = this->_options.baseAddress(); for(typename std::vector::const_iterator it = locations.begin(); it != locations.end(); ++it) { @@ -1215,12 +1241,12 @@ void SplitSegInfoAtom::uleb128EncodeAddresses(const std::vector& lo template -void SplitSegInfoAtom::encode() const +void SplitSegInfoV1Atom::encode() const { // sort into group by pointer adjustment kind std::vector& info = this->_writer._splitSegInfos; for (std::vector::const_iterator it = info.begin(); it != info.end(); ++it) { - this->addSplitSegInfo(it->address, it->kind, it->extra); + this->addSplitSegInfo(it->fixupAddress, it->kind, it->extra); } // delta compress runs of addresses @@ -1298,6 +1324,108 @@ void SplitSegInfoAtom::encode() const _64bitPointerLocations.clear(); } + +template +class SplitSegInfoV2Atom : public LinkEditAtom +{ +public: + SplitSegInfoV2Atom(const Options& opts, ld::Internal& state, OutputFile& writer) + : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } + + // overrides of ld::Atom + virtual const char* name() const { return "split seg info"; } + // overrides of LinkEditAtom + virtual void encode() const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + + typedef uint32_t SectionIndexes; + typedef std::map > FromOffsetMap; + typedef std::map ToOffsetMap; + typedef std::map WholeMap; + + + static ld::Section _s_section; +}; + +template +ld::Section SplitSegInfoV2Atom::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true); + + +template +void SplitSegInfoV2Atom::encode() const +{ + // sort into group by adjustment kind + //fprintf(stderr, "_splitSegV2Infos.size=%lu\n", this->_writer._splitSegV2Infos.size()); + WholeMap whole; + for (const OutputFile::SplitSegInfoV2Entry& entry : this->_writer._splitSegV2Infos) { + //fprintf(stderr, "from=%d, to=%d\n", entry.fixupSectionIndex, entry.targetSectionIndex); + SectionIndexes index = entry.fixupSectionIndex << 16 | entry.targetSectionIndex; + ToOffsetMap& toOffsets = whole[index]; + FromOffsetMap& fromOffsets = toOffsets[entry.targetSectionOffset]; + fromOffsets[entry.referenceKind].push_back(entry.fixupSectionOffset); + } + + // Add marker that this is V2 data + this->_encodedData.reserve(8192); + this->_encodedData.append_byte(DYLD_CACHE_ADJ_V2_FORMAT); + + // stream out + // Whole :== FromToSection+ + this->_encodedData.append_uleb128(whole.size()); + for (auto& fromToSection : whole) { + uint8_t fromSectionIndex = fromToSection.first >> 16; + uint8_t toSectionIndex = fromToSection.first & 0xFFFF; + ToOffsetMap& toOffsets = fromToSection.second; + // FromToSection :== ToOffset+ + this->_encodedData.append_uleb128(fromSectionIndex); + this->_encodedData.append_uleb128(toSectionIndex); + this->_encodedData.append_uleb128(toOffsets.size()); + //fprintf(stderr, "from sect=%d, to sect=%d, count=%lu\n", fromSectionIndex, toSectionIndex, toOffsets.size()); + uint64_t lastToOffset = 0; + for (auto& fromToOffsets : toOffsets) { + uint64_t toSectionOffset = fromToOffsets.first; + FromOffsetMap& fromOffsets = fromToOffsets.second; + // ToOffset :== FromOffset+ + this->_encodedData.append_uleb128(toSectionOffset - lastToOffset); + this->_encodedData.append_uleb128(fromOffsets.size()); + for (auto& kindAndOffsets : fromOffsets) { + uint8_t kind = kindAndOffsets.first; + std::vector& fromOffsets = kindAndOffsets.second; + // FromOffset :== + this->_encodedData.append_uleb128(kind); + this->_encodedData.append_uleb128(fromOffsets.size()); + std::sort(fromOffsets.begin(), fromOffsets.end()); + uint64_t lastFromOffset = 0; + for (uint64_t offset : fromOffsets) { + this->_encodedData.append_uleb128(offset - lastFromOffset); + lastFromOffset = offset; + } + } + lastToOffset = toSectionOffset; + } + } + + + // always add zero byte to mark end + this->_encodedData.append_byte(0); + + // align to pointer size + this->_encodedData.pad_to_size(sizeof(pint_t)); + + this->_encoded = true; +} + + + template class FunctionStartsAtom : public LinkEditAtom { @@ -1497,63 +1625,6 @@ void DataInCodeAtom::encode() const -// linker needs to cache "Designated Requirements" in linked binary -template -class DependentDRAtom : public LinkEditAtom -{ -public: - DependentDRAtom(const Options& opts, ld::Internal& state, OutputFile& writer) - : LinkEditAtom(opts, state, writer, _s_section, sizeof(pint_t)) { } - - // overrides of ld::Atom - virtual const char* name() const { return "dependent dylib DR info"; } - // overrides of LinkEditAtom - virtual void encode() const; - -private: - typedef typename A::P P; - typedef typename A::P::E E; - typedef typename A::P::uint_t pint_t; - - static ld::Section _s_section; - -}; - -template -ld::Section DependentDRAtom::_s_section("__LINKEDIT", "__dependentDR", ld::Section::typeLinkEdit, true); - - -template -void DependentDRAtom::encode() const -{ - Security::SuperBlobCore, Security::kSecCodeMagicDRList, uint32_t>::Maker maker; - - uint32_t index = 0; - for(std::vector::iterator it=_state.dylibs.begin(); it != _state.dylibs.end(); ++it) { - const ld::dylib::File* dylib = *it; - Security::BlobCore* dylibDR = (Security::BlobCore*)dylib->codeSignatureDR(); - void* dup = NULL; - if ( dylibDR != NULL ) { - // Maker takes ownership of every blob added - // We need to make a copy here because dylib still owns the pointer returned by codeSignatureDR() - dup = ::malloc(dylibDR->length()); - ::memcpy(dup, dylibDR, dylibDR->length()); - } - maker.add(index, (Security::BlobCore*)dup); - ++index; - } - - Security::SuperBlob* topBlob = maker.make(); - const uint8_t* data = (uint8_t*)topBlob->data(); - for(size_t i=0; i < topBlob->length(); ++i) - _encodedData.append_byte(data[i]); - - this->_encodedData.pad_to_size(sizeof(pint_t)); - - this->_encoded = true; -} - - template class OptimizationHintsAtom : public LinkEditAtom diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp index 3389f5c..ee1c207 100644 --- a/src/ld/LinkEditClassic.hpp +++ b/src/ld/LinkEditClassic.hpp @@ -1614,9 +1614,17 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSection* { int len = 0; uint32_t otherHalf = 0; - uint32_t value = entry.toTarget->finalAddress()+entry.toAddend; - if ( entry.fromTarget != NULL ) - value -= (entry.fromTarget->finalAddress()+entry.fromAddend); + uint32_t value; + if ( entry.fromTarget != NULL ) { + // this is a sect-diff + value = (entry.toTarget->finalAddress()+entry.toAddend) - (entry.fromTarget->finalAddress()+entry.fromAddend); + } + else { + // this is an absolute address + value = entry.toAddend; + if ( !external ) + value += entry.toTarget->finalAddress(); + } switch ( entry.kind ) { case ld::Fixup::kindStoreARMLow16: len = 0; diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 228a0df..a638cb4 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -36,12 +36,19 @@ #include #include +#include +#include #include "Options.h" #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" #include "Snapshot.h" + +// from FunctionNameDemangle.h +extern "C" size_t fnd_get_demangled_name(const char *mangledName, char *outputBuffer, size_t length); + + // upward dependency on lto::version() namespace lto { extern const char* version(); @@ -143,11 +150,11 @@ Options::Options(int argc, const char* argv[]) fCommonsMode(kCommonsIgnoreDylibs), fUUIDMode(kUUIDContent), fLocalSymbolHandling(kLocalSymbolsAll), fWarnCommons(false), fVerbose(false), fKeepRelocations(false), fWarnStabs(false), fTraceDylibSearching(false), fPause(false), fStatistics(false), fPrintOptions(false), - fSharedRegionEligible(false), fPrintOrderFileStatistics(false), + fSharedRegionEligible(false), fSharedRegionEligibleForceOff(false), fPrintOrderFileStatistics(false), fReadOnlyx86Stubs(false), fPositionIndependentExecutable(false), fPIEOnCommandLine(false), fDisablePositionIndependentExecutable(false), fMaxMinimumHeaderPad(false), fDeadStripDylibs(false), fAllowTextRelocs(false), fWarnTextRelocs(false), fKextsUseStubs(false), - fUsingLazyDylibLinking(false), fEncryptable(true), + fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), fAllowCpuSubtypeMismatches(false), fUseSimplifiedDylibReExports(false), @@ -171,16 +178,19 @@ Options::Options(int argc, const char* argv[]) fEntryPointLoadCommandForceOn(false), fEntryPointLoadCommandForceOff(false), fSourceVersionLoadCommand(false), fSourceVersionLoadCommandForceOn(false), fSourceVersionLoadCommandForceOff(false), - fDependentDRInfo(false), fDependentDRInfoForcedOn(false), fDependentDRInfoForcedOff(false), fTargetIOSSimulator(false), fExportDynamic(false), fAbsoluteSymbols(false), fAllowSimulatorToLinkWithMacOSX(false), fKeepDwarfUnwind(true), fKeepDwarfUnwindForcedOn(false), fKeepDwarfUnwindForcedOff(false), fVerboseOptimizationHints(false), fIgnoreOptimizationHints(false), fGenerateDtraceDOF(true), fAllowBranchIslands(true), fTraceSymbolLayout(false), - fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), - fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), - fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), - fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), + fMarkAppExtensionSafe(false), fCheckAppExtensionSafe(false), fForceLoadSwiftLibs(false), + fSharedRegionEncodingV2(false), fUseDataConstSegment(false), + fUseDataConstSegmentForceOn(false), fUseDataConstSegmentForceOff(false), + fBundleBitcode(false), fHideSymbols(false), fReverseMapUUIDRename(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), + fIgnoreAutoLink(false), fPlatform(kPlatformUnknown), + fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), + fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), + fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1) { this->checkForClassic(argc, argv); @@ -236,7 +246,7 @@ bool Options::interposable(const char* name) const bool Options::printWhyLive(const char* symbolName) const { - return ( fWhyLive.find(symbolName) != fWhyLive.end() ); + return fWhyLive.contains(symbolName); } @@ -325,7 +335,7 @@ uint32_t Options::maxSegProtection(const char* segName) const { // iPhoneOS always uses same protection for max and initial // simulator apps need to use MacOSX max-prot - if ( (fIOSVersionMin != ld::iOSVersionUnset) && (fArchitecture != CPU_TYPE_I386) ) + if ( (fPlatform != kPlatformOSX) && !fTargetIOSSimulator ) return initialSegProtection(segName); for(std::vector::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) { @@ -391,6 +401,17 @@ uint8_t Options::customSectionAlignment(const char* segName, const char* sectNam return 0; } +bool Options::segmentOrderAfterFixedAddressSegment(const char* segName) const +{ + bool nowPinned = false; + for (std::vector::const_iterator it=fSegmentOrder.begin(); it != fSegmentOrder.end(); ++it) { + if ( strcmp(*it, segName) == 0 ) + return nowPinned; + if ( hasCustomSegmentAddress(*it) ) + nowPinned = true; + } + return false; +} bool Options::hasExportedSymbolOrder() { @@ -549,9 +570,26 @@ const std::vector* Options::sectionOrder(const char* segName) const return NULL; } +uint32_t Options::minOSversion() const +{ + switch (fPlatform) { + case kPlatformiOS: + return iOSVersionMin(); + case kPlatformOSX: + return macosxVersionMin(); + case kPlatformWatchOS: + return watchOSVersionMin(); +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + return iOSVersionMin(); +#endif + default: + break; + } + return 0; +} - -void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) +void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype, Options::Platform platform) { for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { if ( (type == t->cpuType) && (subtype == t->cpuSubType) ) { @@ -560,10 +598,11 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) fArchitectureName = t->archName; fHasPreferredSubType = t->isSubType; fArchSupportsThumb2 = t->supportsThumb2; + fPlatform = platform; switch ( type ) { case CPU_TYPE_I386: case CPU_TYPE_X86_64: - if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + if ( (fPlatform == kPlatformOSX) && (fOutputKind != Options::kObjectFile) ) { #ifdef DEFAULT_MACOSX_MIN_VERSION warning("-macosx_version_min not specified, assuming " DEFAULT_MACOSX_MIN_VERSION); setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); @@ -575,7 +614,7 @@ void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype) break; case CPU_TYPE_ARM: case CPU_TYPE_ARM64: - if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) { + if ( (fPlatform == kPlatformiOS) && (fOutputKind != Options::kObjectFile) ) { #if defined(DEFAULT_IPHONEOS_MIN_VERSION) warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); @@ -672,6 +711,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; + if ( checkForFile("%s/lib%s.tbd", dir, rootName, result) ) + return result; if ( checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; } @@ -701,6 +742,8 @@ Options::FileInfo Options::findLibrary(const char* rootName, bool dylibsOnly) co it != fLibrarySearchPaths.end(); it++) { const char* dir = *it; + if ( lookForDylibs && checkForFile("%s/lib%s.tbd", dir, rootName, result) ) + return result; if ( lookForDylibs && checkForFile("%s/lib%s.dylib", dir, rootName, result) ) return result; if ( lookForDylibs && checkForFile("%s/lib%s.so", dir, rootName, result) ) @@ -732,31 +775,21 @@ Options::FileInfo Options::findFramework(const char* frameworkName) const Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const { - for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - // ??? Shouldn't we be using String here and just initializing it? - // ??? Use str.c_str () to pull out the string for the stat call. - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, rootName); - strcat(possiblePath, ".framework/"); - strcat(possiblePath, rootName); - if ( suffix != NULL ) { + for (const auto* path : fFrameworkSearchPaths) { + auto possiblePath = std::string(path).append("/").append(rootName).append(".framework/").append(rootName); + if ( suffix != nullptr ) { char realPath[PATH_MAX]; // no symlink in framework to suffix variants, so follow main symlink - if ( realpath(possiblePath, realPath) != NULL ) { - strcpy(possiblePath, realPath); - strcat(possiblePath, suffix); - } + if ( realpath(possiblePath.c_str(), realPath) != nullptr ) + possiblePath = std::string(realPath).append(suffix); } FileInfo result; - bool found = result.checkFileExists(*this, possiblePath); + bool found = result.checkFileExists(*this, (possiblePath + ".tbd").c_str()); + if ( !found ) + found = result.checkFileExists(*this, possiblePath.c_str()); if ( fTraceDylibSearching ) printf("[Logging for XBS]%sfound framework: '%s'\n", - (found ? " " : " not "), possiblePath); + (found ? " " : " not "), possiblePath.c_str()); if ( found ) { return result; } @@ -768,68 +801,84 @@ Options::FileInfo Options::findFramework(const char* rootName, const char* suffi throwf("framework not found %s", rootName); } -Options::FileInfo Options::findFile(const char* path) const +Options::FileInfo Options::findFile(const std::string &path) const { FileInfo result; - // if absolute path and not a .o file, the use SDK prefix - if ( (path[0] == '/') && (strcmp(&path[strlen(path)-2], ".o") != 0) ) { - const int pathLen = strlen(path); - for (std::vector::const_iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) { - // ??? Shouldn't we be using String here? - const char* sdkPathDir = *it; - const int sdkPathDirLen = strlen(sdkPathDir); - char possiblePath[sdkPathDirLen+pathLen+4]; - strcpy(possiblePath, sdkPathDir); - if ( possiblePath[sdkPathDirLen-1] == '/' ) - possiblePath[sdkPathDirLen-1] = '\0'; - strcat(possiblePath, path); - if ( result.checkFileExists(*this, possiblePath) ) { + // if absolute path and not a .o file, then use SDK prefix + if ( (path[0] == '/') && (strcmp(&path[path.size()-2], ".o") != 0) ) { + auto tbdFile = path; + auto lastSlashIdx = tbdFile.find_last_of('/'); + auto lastDotIdx = tbdFile.find_last_of('.'); + if (lastDotIdx != std::string::npos && lastDotIdx > lastSlashIdx) + tbdFile.erase(lastDotIdx, std::string::npos); + tbdFile.append(".tbd"); + + for (const auto* sdkPathDir : fSDKPaths) { + auto possiblePath = std::string(sdkPathDir) + tbdFile; + if ( result.checkFileExists(*this, possiblePath.c_str()) ) + return result; + possiblePath = std::string(sdkPathDir) + path; + if ( result.checkFileExists(*this, possiblePath.c_str()) ) return result; - } } } // try raw path - if ( result.checkFileExists(*this, path) ) { + { + std::string file = path; + auto lastDotIdx = file.find_last_of('.'); + if (lastDotIdx != std::string::npos) + file.erase(lastDotIdx, std::string::npos); + if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) + return result; + } + if ( result.checkFileExists(*this, path.c_str()) ) { return result; } + // try @executable_path substitution - if ( (strncmp(path, "@executable_path/", 17) == 0) && (fExecutablePath != NULL) ) { - char newPath[strlen(fExecutablePath) + strlen(path)]; + if ( (path.find("@executable_path/") == 0) && (fExecutablePath != nullptr) ) { + char newPath[strlen(fExecutablePath) + path.size()]; strcpy(newPath, fExecutablePath); char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) + if ( addPoint != nullptr ) strcpy(&addPoint[1], &path[17]); else strcpy(newPath, &path[17]); + + std::string file = newPath; + auto lastDotIdx = file.find_last_of('.'); + if (lastDotIdx != std::string::npos) + file.erase(lastDotIdx, std::string::npos); + if ( result.checkFileExists(*this, file.append(".tbd").c_str()) ) { + return result; + } if ( result.checkFileExists(*this, newPath) ) { return result; } } // not found - throwf("file not found: %s", path); + throwf("file not found: %s", path.c_str()); } -Options::FileInfo Options::findFileUsingPaths(const char* path) const +Options::FileInfo Options::findFileUsingPaths(const std::string &path) const { FileInfo result; - const char* lastSlash = strrchr(path, '/'); - const char* leafName = (lastSlash == NULL) ? path : &lastSlash[1]; + auto lastSlashPos = path.find_last_of('/'); + auto pos = ( lastSlashPos != std::string::npos ) ? lastSlashPos + 1 : 0; + auto leafName = path.substr(pos); // Is this in a framework? // /path/Foo.framework/Foo ==> true (Foo) // /path/Foo.framework/Frameworks/Bar.framework/Bar ==> true (Bar) // /path/Foo.framework/Resources/Bar ==> false bool isFramework = false; - if ( lastSlash != NULL ) { - char frameworkDir[strlen(leafName) + 20]; - strcpy(frameworkDir, "/"); - strcat(frameworkDir, leafName); - strcat(frameworkDir, ".framework/"); - if ( strstr(path, frameworkDir) != NULL ) + if ( lastSlashPos != std::string::npos ) { + auto frameworkDir = std::string("/").append(leafName).append(".framework/"); + if ( path.rfind(frameworkDir) != std::string::npos ) isFramework = true; } @@ -838,35 +887,28 @@ Options::FileInfo Options::findFileUsingPaths(const char* path) const // don't need to try variations, just paths. We do need to add the additional bits // onto the framework path though. if ( isFramework ) { - for (std::vector::const_iterator it = fFrameworkSearchPaths.begin(); - it != fFrameworkSearchPaths.end(); - it++) { - const char* dir = *it; - char possiblePath[PATH_MAX]; - strcpy(possiblePath, dir); - strcat(possiblePath, "/"); - strcat(possiblePath, leafName); - strcat(possiblePath, ".framework"); - - //fprintf(stderr,"Finding Framework: %s/%s, leafName=%s\n", possiblePath, leafName, leafName); - if ( checkForFile("%s/%s", possiblePath, leafName, result) ) + auto endPos = path.rfind(".framework"); + auto beginPos = path.find_last_of('/', endPos); + auto leafPath = path.substr(beginPos); + for (const auto* dir : fFrameworkSearchPaths) { + auto possiblePath = dir + leafPath; + if ( checkForFile("%s.%s", possiblePath.c_str(), "tbd", result) ) + return result; + if ( checkForFile("%s", possiblePath.c_str(), "", result) ) return result; } - } - else { + } else { // if this is a .dylib inside a framework, do not search -L paths - // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard - int leafLen = strlen(leafName); - bool embeddedDylib = ( (leafLen > 6) - && (strcmp(&leafName[leafLen-6], ".dylib") == 0) - && (strstr(path, ".framework/") != NULL) ); + // ld64's re-export cycle detection logic prevents use of X11 libGL on Leopard + bool embeddedDylib = ( (leafName.size() > 6) + && (leafName.find(".dylib", leafName.size()-6) != std::string::npos) + && (path.find(".framework/") != std::string::npos) ); if ( !embeddedDylib ) { - for (std::vector::const_iterator it = fLibrarySearchPaths.begin(); - it != fLibrarySearchPaths.end(); - it++) { - const char* dir = *it; + for (const auto* dir : fLibrarySearchPaths) { //fprintf(stderr,"Finding Library: %s/%s\n", dir, leafName); - if ( checkForFile("%s/%s", dir, leafName, result) ) + if ( checkForFile("%s/%s", dir, std::string(leafName).append(".tbd").c_str(), result) ) + return result; + if ( checkForFile("%s/%s", dir, leafName.c_str(), result) ) return result; } } @@ -1070,6 +1112,23 @@ bool Options::SetWithWildcards::containsNonWildcard(const char* symbol) const } +std::vector Options::exportsData() const +{ + return fExportSymbols.data(); +} + + +std::vector Options::SetWithWildcards::data() const +{ + std::vector data; + for (NameSet::iterator it=regularBegin(); it != regularEnd(); ++it) { + data.push_back(*it); + } + for (std::vector::const_iterator it=fWildCard.begin(); it != fWildCard.end(); ++it) { + data.push_back(*it); + } + return data; +} bool Options::SetWithWildcards::inCharRange(const char*& p, unsigned char c) const { @@ -1348,6 +1407,7 @@ void Options::setMacOSXVersionMin(const char* version) minorVersion = 255; } fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8)); + fPlatform = kPlatformOSX; } else { warning("unknown option to -macosx_version_min, not 10.x"); @@ -1368,18 +1428,52 @@ void Options::setIOSVersionMin(const char* version) unsigned int majorVersion = version[0] - '0'; unsigned int minorVersion = version[2] - '0'; fIOSVersionMin = (ld::IOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + fPlatform = kPlatformiOS; +} + + +void Options::setWatchOSVersionMin(const char* version) +{ + if ( version == NULL ) + throw "-watchos_version_min argument missing"; + if ( ! isdigit(version[0]) ) + throw "-watchos_version_min argument is not a number"; + if ( version[1] != '.' ) + throw "-watchos_version_min argument is missing period as second character"; + if ( ! isdigit(version[2]) ) + throw "-watchos_version_min argument is not a number"; + + unsigned int majorVersion = version[0] - '0'; + unsigned int minorVersion = version[2] - '0'; + fWatchOSVersionMin = (ld::WatchOSVersionMin)((majorVersion << 16) | (minorVersion << 8)); + fPlatform = kPlatformWatchOS; } + bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IOSVersionMin requirediPhoneOSMin) { if ( fMacVersionMin != ld::macVersionUnset ) { return ( fMacVersionMin >= requiredMacMin ); } + else if ( fWatchOSVersionMin != ld::wOSVersionUnset ) { + // Hack until we fully track watch and ios versions seperately + return ( (fWatchOSVersionMin + 0x00070000) >= requirediPhoneOSMin); + } else { - return ( fIOSVersionMin >= requirediPhoneOSMin); + return ( fIOSVersionMin >= requirediPhoneOSMin ); } } +bool Options::min_iOS(ld::IOSVersionMin requirediOSMin) +{ + if ( fWatchOSVersionMin != ld::wOSVersionUnset ) { + // Hack until we fully track watch and ios versions seperately + return ( (fWatchOSVersionMin + 0x00070000) >= requirediOSMin); + } + else { + return ( fIOSVersionMin >= requirediOSMin ); + } +} void Options::setWeakReferenceMismatchTreatment(const char* treatment) { @@ -1766,7 +1860,7 @@ void Options::addSection(const char* segment, const char* section, const char* p ::close(fd); // record section to create - ExtraSection info = { segment, section, path, (uint8_t*)p, stat_buf.st_size }; + ExtraSection info = { segment, section, path, (uint8_t*)p, (uint64_t)stat_buf.st_size }; fExtraSections.push_back(info); } @@ -1895,7 +1989,188 @@ void Options::warnObsolete(const char* arg) } +void Options::cannotBeUsedWithBitcode(const char* arg) +{ + if ( fBundleBitcode ) + throwf("%s and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together", arg); +} + +std::string Options::getVersionString32(uint32_t ver) const +{ + if (ver == 0 || ver >= 0x10000000) + return "0.0.0"; + + unsigned microVersion = ver & 0xFF; + unsigned minorVersion = (ver >> 8) & 0xFF; + unsigned majorVersion = (ver >> 16) & 0xFF; + std::stringstream versionString; + versionString << majorVersion << "." << minorVersion << "." << microVersion; + return versionString.str(); +} + +std::string Options::getVersionString64(uint64_t ver) const +{ + uint64_t a = (ver >> 40) & 0xFFFFFF; + uint64_t b = (ver >> 30) & 0x3FF; + uint64_t c = (ver >> 20) & 0x3FF; + uint64_t d = (ver >> 10) & 0x3FF; + uint64_t e = ver & 0x3FF; + std::stringstream versionString; + versionString << a << "." << b << "." << c << "." << d << "." << e; + return versionString.str(); +} + +std::string Options::getSDKVersionStr() const +{ + return getVersionString32(fSDKVersion); +} + +std::string Options::getPlatformStr() const +{ + switch (fPlatform) { + case Options::kPlatformOSX: + return "MacOSX"; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + return "iPhoneSimulator"; + else + return "iPhoneOS"; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + return "watchOS Simulator"; + else + return "watchOS"; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + return "AppleTVSimulator"; + else + return "AppleTVOS"; + break; +#endif + case Options::kPlatformUnknown: + return "Unknown"; + } +} + +std::vector Options::writeBitcodeLinkOptions() const +{ + std::vector linkCommand; + switch ( fOutputKind ) { + case Options::kDynamicLibrary: + linkCommand.push_back("-dylib"); + linkCommand.push_back("-compatibility_version"); + if ( fDylibCompatVersion != 0 ) { + linkCommand.push_back(getVersionString32(fDylibCompatVersion)); + } else { + linkCommand.push_back(getVersionString32(currentVersion32())); + } + if ( fDylibCurrentVersion != 0 ) { + linkCommand.push_back("-current_version"); + linkCommand.push_back(getVersionString64(fDylibCurrentVersion)); + } + linkCommand.push_back("-install_name"); + linkCommand.push_back(installPath()); + break; + case Options::kDynamicExecutable: + linkCommand.push_back("-execute"); + break; + case Options::kObjectFile: + linkCommand.push_back("-r"); + break; + default: + throwf("could not write bitcode options file output kind\n"); + } + if (!fImplicitlyLinkPublicDylibs) + linkCommand.push_back("-no_implicit_dylibs"); + + // Add deployment target. + // Platform is allowed to be unknown for "ld -r". + switch (fPlatform) { + case Options::kPlatformOSX: + linkCommand.push_back("-macosx_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fMacVersionMin)); + break; + case Options::kPlatformiOS: + if (targetIOSSimulator()) + linkCommand.push_back("-ios_simulator_version_min"); + else + linkCommand.push_back("-ios_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; + case Options::kPlatformWatchOS: + if (targetIOSSimulator()) + linkCommand.push_back("-watchos_simulator_version_min"); + else + linkCommand.push_back("-watchos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + if (targetIOSSimulator()) + linkCommand.push_back("-tvos_simulator_version_min"); + else + linkCommand.push_back("-tvos_version_min"); + linkCommand.push_back(getVersionString32((unsigned)fIOSVersionMin)); + break; +#endif + case Options::kPlatformUnknown: + if ( fOutputKind != Options::kObjectFile ) { + throwf("platform is unknown for final bitcode bundle," + "deployment target and min version is required for -bitcode_bundle"); + } + break; + } + + + // entry name + if ( fEntryName ) { + linkCommand.push_back("-e"); + linkCommand.push_back(fEntryName); + } + + // Write rpaths + if (!fRPaths.empty()) { + for (std::vector::const_iterator it=fRPaths.begin(); it != fRPaths.end(); ++it) { + linkCommand.push_back("-rpath"); + linkCommand.push_back(*it); + } + } + + // Other bitcode compatiable options + if ( fObjCABIVersion1Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("1"); + } else if ( fObjCABIVersion2Override ) { + linkCommand.push_back("-objc_abi_version"); + linkCommand.push_back("2"); + } + if ( fExecutablePath ) { + linkCommand.push_back("-executable_path"); + linkCommand.push_back(fExecutablePath); + } + if ( fDeadStrip ) + linkCommand.push_back("-dead_strip"); + if ( fExportDynamic ) + linkCommand.push_back("-export_dynamic"); + if ( fMarkAppExtensionSafe && fCheckAppExtensionSafe ) + linkCommand.push_back("-application_extension"); + + if ( fSourceVersionLoadCommandForceOn ) + linkCommand.push_back("-add_source_version"); + if ( fSourceVersion != 0 ) { + linkCommand.push_back("-source_version"); + linkCommand.push_back(getVersionString64(fSourceVersion)); + } + + // linker flag added by swift driver + // rdar://problem/20108072 + if ( !fObjcCategoryMerging ) + linkCommand.push_back("-no_objc_category_merging"); + + return linkCommand; +} // // Process all command line arguments. @@ -1957,15 +2232,18 @@ void Options::parse(int argc, const char* argv[]) if ( (fOutputKind != kObjectFile) && (fOutputKind != kKextBundle) ) { fOutputKind = kStaticExecutable; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dylib") == 0 ) { fOutputKind = kDynamicLibrary; } else if ( strcmp(arg, "-bundle") == 0 ) { fOutputKind = kDynamicBundle; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dylinker") == 0 ) { fOutputKind = kDyld; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-execute") == 0 ) { if ( fOutputKind != kStaticExecutable ) @@ -1973,12 +2251,14 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-preload") == 0 ) { fOutputKind = kPreload; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-r") == 0 ) { fOutputKind = kObjectFile; } else if ( strcmp(arg, "-kext") == 0 ) { fOutputKind = kKextBundle; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-o") == 0 ) { snapshotArgCount = 0; @@ -1992,6 +2272,7 @@ void Options::parse(int argc, const char* argv[]) info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-lto_library") == 0 ) { snapshotFileArgIndex = 1; @@ -2018,18 +2299,21 @@ void Options::parse(int argc, const char* argv[]) // Avoid lazy binding. else if ( strcmp(arg, "-bind_at_load") == 0 ) { fBindAtLoad = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-twolevel_namespace") == 0 ) { fNameSpace = kTwoLevelNameSpace; } else if ( strcmp(arg, "-flat_namespace") == 0 ) { fNameSpace = kFlatNameSpace; + cannotBeUsedWithBitcode(arg); } // Also sets a bit to ensure dyld causes everything // in the namespace to be flat. // ??? Deprecate else if ( strcmp(arg, "-force_flat_namespace") == 0 ) { fNameSpace = kForceFlatNameSpace; + cannotBeUsedWithBitcode(arg); } // Similar to --whole-archive. else if ( strcmp(arg, "-all_load") == 0 ) { @@ -2070,13 +2354,16 @@ void Options::parse(int argc, const char* argv[]) snapshotFileArgIndex = 3; parseSectionOrderFile(argv[i+1], argv[i+2], argv[i+3]); i += 3; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-order_file") == 0 ) { snapshotFileArgIndex = 1; parseOrderFile(argv[++i], false); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-order_file_statistics") == 0 ) { fPrintOrderFileStatistics = true; + cannotBeUsedWithBitcode(arg); } // ??? Deprecate segcreate. // -sectcreate puts whole files into a section in the output. @@ -2106,6 +2393,7 @@ void Options::parse(int argc, const char* argv[]) warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment); fBaseAddress = temp; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-e") == 0 ) { fEntryName = argv[++i]; @@ -2120,7 +2408,8 @@ void Options::parse(int argc, const char* argv[]) loadFileList(path, baseOrdinal); } else if ( strcmp(arg, "-keep_private_externs") == 0 ) { - fKeepPrivateExterns = true; + cannotBeUsedWithBitcode(arg); + fKeepPrivateExterns = true; } else if ( strcmp(arg, "-final_output") == 0 ) { fFinalName = argv[++i]; @@ -2137,11 +2426,13 @@ void Options::parse(int argc, const char* argv[]) // do nothing, -interposable_list overrides -interposable" break; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-interposable_list") == 0 ) { snapshotFileArgIndex = 1; fInterposeMode = kInterposeSome; loadExportFile(argv[++i], "-interposable_list", fInterposeList); + cannotBeUsedWithBitcode(arg); } // Default for -interposable/-multi_module/-single_module. else if ( strcmp(arg, "-single_module") == 0 ) { @@ -2160,6 +2451,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbols_list and -exported_symbols_list"; fExportMode = kDontExportSome; loadExportFile(argv[++i], "-unexported_symbols_list", fDontExportSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-exported_symbol") == 0 ) { if ( fExportMode == kDontExportSome ) @@ -2172,6 +2464,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -unexported_symbol and -exported_symbol"; fExportMode = kDontExportSome; fDontExportSymbols.insert(argv[++i]); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-non_global_symbols_no_strip_list") == 0 ) { snapshotFileArgIndex = 1; @@ -2179,6 +2472,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveInclude; loadExportFile(argv[++i], "-non_global_symbols_no_strip_list", fLocalSymbolsIncluded); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-non_global_symbols_strip_list") == 0 ) { snapshotFileArgIndex = 1; @@ -2186,6 +2480,7 @@ void Options::parse(int argc, const char* argv[]) throw "can't use -non_global_symbols_no_strip_list and -non_global_symbols_strip_list"; fLocalSymbolHandling = kLocalSymbolsSelectiveExclude; loadExportFile(argv[++i], "-non_global_symbols_strip_list", fLocalSymbolsExcluded); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate else if ( strcmp(arg, "-no_arch_warnings") == 0 ) { @@ -2194,6 +2489,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) { fForceSubtypeAll = true; fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); } // Similar to -weak-l but uses the absolute path name to the library. else if ( strcmp(arg, "-weak_library") == 0 ) { @@ -2203,6 +2499,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fWeakImport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-lazy_library") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2212,6 +2509,7 @@ void Options::parse(int argc, const char* argv[]) info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-framework") == 0 ) { snapshotArgCount = 0; @@ -2235,6 +2533,7 @@ void Options::parse(int argc, const char* argv[]) info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); fUsingLazyDylibLinking = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-search_paths_first") == 0 ) { // previously handled by buildSearchPaths() @@ -2244,6 +2543,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-undefined") == 0 ) { setUndefinedTreatment(argv[++i]); + cannotBeUsedWithBitcode(arg); } // Debugging output flag. else if ( strcmp(arg, "-arch_multiple") == 0 ) { @@ -2261,10 +2561,12 @@ void Options::parse(int argc, const char* argv[]) case kWarning: fWarnTextRelocs = true; fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); break; case kSuppress: fWarnTextRelocs = false; fAllowTextRelocs = true; + cannotBeUsedWithBitcode(arg); break; case kError: fWarnTextRelocs = false; @@ -2287,6 +2589,7 @@ void Options::parse(int argc, const char* argv[]) // later. Prebinding is less useful on 10.4 and greater. else if ( strcmp(arg, "-prebind") == 0 ) { fPrebind = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-noprebind") == 0 ) { warnObsolete(arg); @@ -2310,6 +2613,7 @@ void Options::parse(int argc, const char* argv[]) // ignore for snapshot because a stub dylib will be created in the snapshot snapshotArgCount = 0; addDylibOverride(argv[++i]); + cannotBeUsedWithBitcode(arg); } // What to expand @executable_path to if found in dependent dylibs else if ( strcmp(arg, "-executable_path") == 0 ) { @@ -2340,6 +2644,7 @@ void Options::parse(int argc, const char* argv[]) warning("alignment for -segalign %s is not a power of two, using 0x%X", size, p2aligned); fSegmentAlignment = p2aligned; } + cannotBeUsedWithBitcode(arg); } // Puts a specified segment at a particular address that must // be a multiple of the segment alignment. @@ -2353,15 +2658,18 @@ void Options::parse(int argc, const char* argv[]) if ( seg.address != temp ) warning("-segaddr %s not %lld byte aligned", seg.name, fSegmentAlignment); fCustomSegmentAddresses.push_back(seg); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_only_addr") == 0 ) { fBaseAddress = parseAddress(argv[++i]); + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we deprecate split-seg. else if ( strcmp(arg, "-segs_read_write_addr") == 0 ) { fBaseWritableAddress = parseAddress(argv[++i]); fSplitSegs = true; + cannotBeUsedWithBitcode(arg); } // ??? Deprecate when we get rid of basing at build time. else if ( strcmp(arg, "-seg_addr_table") == 0 ) { @@ -2370,6 +2678,7 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-seg_addr_table missing argument"; fSegAddrTablePath = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) { warnObsolete(arg); @@ -2383,6 +2692,7 @@ void Options::parse(int argc, const char* argv[]) seg.max = parseProtection(argv[++i]); seg.init = parseProtection(argv[++i]); fCustomSegmentProtections.push_back(seg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-pagezero_size") == 0 ) { const char* size = argv[++i]; @@ -2393,12 +2703,14 @@ void Options::parse(int argc, const char* argv[]) if ( (fZeroPageSize != temp) ) warning("-pagezero_size not page aligned, rounding down"); fZeroPageSize = temp; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-stack_addr") == 0 ) { const char* address = argv[++i]; if ( address == NULL ) throw "-stack_addr missing

"; fStackAddr = parseAddress(address); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-stack_size") == 0 ) { const char* size = argv[++i]; @@ -2411,15 +2723,18 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-allow_stack_execute") == 0 ) { fExecutableStack = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allow_heap_execute") == 0 ) { fDisableNonExecutableHeap = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sectalign") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) || (argv[i+3]==NULL) ) throw "-sectalign missing
"; addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]); i += 3; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sectorder_detail") == 0 ) { warnObsolete(arg); @@ -2475,6 +2790,24 @@ void Options::parse(int argc, const char* argv[]) setIOSVersionMin(argv[++i]); fTargetIOSSimulator = true; } + else if ( strcmp(arg, "-watchos_version_min") == 0 ) { + setWatchOSVersionMin(argv[++i]); + } + else if ( strcmp(arg, "-watchos_simulator_version_min") == 0 ) { + setWatchOSVersionMin(argv[++i]); + fTargetIOSSimulator = true; + } + #if SUPPORT_APPLE_TV + else if ( strcmp(arg, "-tvos_version_min") == 0 ) { + setIOSVersionMin(argv[++i]); + fPlatform = kPlatform_tvOS; + } + else if ( strcmp(arg, "-tvos_simulator_version_min") == 0 ) { + setIOSVersionMin(argv[++i]); + fPlatform = kPlatform_tvOS; + fTargetIOSSimulator = true; + } + #endif else if ( strcmp(arg, "-multiply_defined") == 0 ) { //warnObsolete(arg); ++i; @@ -2515,12 +2848,14 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-u missing argument"; fInitialUndefines.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-U") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-U missing argument"; fAllowedUndefined.insert(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-s") == 0 ) { warnObsolete(arg); @@ -2573,9 +2908,15 @@ void Options::parse(int argc, const char* argv[]) if ( size == NULL ) throw "-headerpad missing argument"; fMinimumHeaderPad = parseAddress(size); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-headerpad_max_install_names") == 0 ) { - fMaxMinimumHeaderPad = true; + // ignore -headerpad_max_install_names when compiling with bitcode + // rdar://problem/20748962 + if ( fBundleBitcode ) + warning("-headerpad_max_install_names is ignored when used with -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES)"); + else + fMaxMinimumHeaderPad = true; } else if ( strcmp(arg, "-t") == 0 ) { fLogAllFiles = true; @@ -2592,6 +2933,7 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-umbrella missing argument"; fUmbrellaName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allowable_client") == 0 ) { const char* name = argv[++i]; @@ -2600,6 +2942,7 @@ void Options::parse(int argc, const char* argv[]) throw "-allowable_client missing argument"; fAllowableClients.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-client_name") == 0 ) { const char* name = argv[++i]; @@ -2608,30 +2951,35 @@ void Options::parse(int argc, const char* argv[]) throw "-client_name missing argument"; fClientName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sub_umbrella") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-sub_umbrella missing argument"; fSubUmbellas.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sub_library") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-sub_library missing argument"; fSubLibraries.push_back(name); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-init") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-init missing argument"; fInitFunctionName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dot") == 0 ) { const char* name = argv[++i]; if ( name == NULL ) throw "-dot missing argument"; fDotOutputFile = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-warn_commons") == 0 ) { fWarnCommons = true; @@ -2665,11 +3013,17 @@ void Options::parse(int argc, const char* argv[]) ++i; // previously handled by buildSearchPaths() } + else if ( strcmp(arg, "-bitcode_bundle") == 0 ) { + snapshotArgCount = 0; + // previously handled by buildSearchPaths() + } else if ( strcmp(arg, "-no_uuid") == 0 ) { fUUIDMode = kUUIDNone; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-random_uuid") == 0 ) { fUUIDMode = kUUIDRandom; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dtrace") == 0 ) { snapshotFileArgIndex = 1; @@ -2677,6 +3031,7 @@ void Options::parse(int argc, const char* argv[]) if ( name == NULL ) throw "-dtrace missing argument"; fDtraceScriptName = name; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-root_safe") == 0 ) { fRootSafe = true; @@ -2693,14 +3048,45 @@ void Options::parse(int argc, const char* argv[]) if ( pair.alias == NULL ) throw "missing argument to -alias"; fAliases.push_back(pair); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-alias_list") == 0 ) { snapshotFileArgIndex = 1; parseAliasFile(argv[++i]); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-save-temps") == 0 ) { fSaveTempFiles = true; } + else if ( strcmp(arg, "-bitcode_hide_symbols") == 0 ) { + fHideSymbols = true; + if ( !fBundleBitcode ) + warning("-bitcode_hide_symbols is ignored without -bitcode_bundle"); + } + else if ( strcmp(arg, "-bitcode_symbol_map") == 0) { + fReverseMapPath = argv[++i]; + if ( fReverseMapPath == NULL ) + throw "missing argument to -bitcode_symbol_map"; + struct stat statbuf; + ::stat(fReverseMapPath, &statbuf); + if (S_ISDIR(statbuf.st_mode)) { + char tempPath[PATH_MAX]; + sprintf(tempPath, "%s/XXXXXX", fReverseMapPath); + int tempFile = ::mkstemp(tempPath); + if (tempFile == -1) + throwf("could not write file to symbol map directory: %s", fReverseMapPath); + ::close(tempFile); + fReverseMapTempPath = std::string(tempPath); + fReverseMapUUIDRename = true; + } else + fReverseMapTempPath = std::string(fReverseMapPath); + } + else if ( strcmp(argv[i], "-flto-codegen-only") == 0) { + fLTOCodegenOnly = true; + } + else if ( strcmp(argv[i], "-ignore_auto_link") == 0) { + fIgnoreAutoLink = true; + } else if ( strcmp(arg, "-rpath") == 0 ) { const char* path = argv[++i]; if ( path == NULL ) @@ -2724,6 +3110,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-no_pie") == 0 ) { fDisablePositionIndependentExecutable = true; + cannotBeUsedWithBitcode(arg); } else if ( strncmp(arg, "-reexport-l", 11) == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2732,6 +3119,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexport_library") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2740,6 +3128,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexport_framework") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2748,6 +3137,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fReExport = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strncmp(arg, "-upward-l", 9) == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2756,6 +3146,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fUpward = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-upward_library") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2764,6 +3155,7 @@ void Options::parse(int argc, const char* argv[]) info.options.fUpward = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-upward_framework") == 0 ) { // SNAPSHOT FIXME: what should we do for link snapshots? (ignore for now) @@ -2772,9 +3164,11 @@ void Options::parse(int argc, const char* argv[]) info.options.fUpward = true; info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i); addLibrary(info); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-dead_strip_dylibs") == 0 ) { fDeadStripDylibs = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_implicit_dylibs") == 0 ) { fImplicitlyLinkPublicDylibs = false; @@ -2783,28 +3177,38 @@ void Options::parse(int argc, const char* argv[]) // ignore } else if ( strcmp(arg, "-no_encryption") == 0 ) { - fEncryptable = false; + fEncryptableForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-encryptable") == 0 ) { + fEncryptableForceOn = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_compact_unwind") == 0 ) { fAddCompactUnwindEncoding = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mllvm") == 0 ) { const char* opts = argv[++i]; if ( opts == NULL ) throw "missing argument to -mllvm"; fLLVMOptions.push_back(opts); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mcpu") == 0 ) { const char* cpu = argv[++i]; if ( cpu == NULL ) throw "missing argument to -mcpu"; fLtoCpu = cpu; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_order_inits") == 0 ) { fAutoOrderInitializers = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_order_data") == 0 ) { fOrderData = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-seg_page_size") == 0 ) { SegmentSize seg; @@ -2816,31 +3220,38 @@ void Options::parse(int argc, const char* argv[]) if ( (seg.size != temp) ) warning("-seg_page_size %s not 4K aligned, rounding down", seg.name); fCustomSegmentSizes.push_back(seg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-mark_dead_strippable_dylib") == 0 ) { fMarkDeadStrippableDylib = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-exported_symbols_order") == 0 ) { snapshotFileArgIndex = 1; loadSymbolOrderFile(argv[++i], fExportSymbolsOrder); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) { warnObsolete("-no_compact_linkedit"); } else if ( strcmp(arg, "-no_eh_labels") == 0 ) { fNoEHLabels = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-warn_compact_unwind") == 0 ) { fWarnCompactUnwind = true; } else if ( strcmp(arg, "-allow_sub_type_mismatches") == 0 ) { fAllowCpuSubtypeMismatches = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) { fOptimizeZeroFill = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-merge_zero_fill_sections") == 0 ) { fMergeZeroFill = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_abi_version") == 0 ) { const char* version = argv[++i]; @@ -2862,6 +3273,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { fObjcGcCompaction = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_gc") == 0 ) { fObjCGc = true; @@ -2869,6 +3281,7 @@ void Options::parse(int argc, const char* argv[]) warning("-objc_gc overriding -objc_gc_only"); fObjCGcOnly = false; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-objc_gc_only") == 0 ) { fObjCGcOnly = true; @@ -2876,6 +3289,7 @@ void Options::parse(int argc, const char* argv[]) warning("-objc_gc_only overriding -objc_gc"); fObjCGc = false; } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-demangle") == 0 ) { fDemangle = true; @@ -2887,6 +3301,7 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-no_version_load_command") == 0 ) { fVersionLoadCommandForcedOff = true; fVersionLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-function_starts") == 0 ) { fFunctionStartsForcedOn = true; @@ -2895,10 +3310,12 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-no_function_starts") == 0 ) { fFunctionStartsForcedOff = true; fFunctionStartsForcedOn = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_data_in_code_info") == 0 ) { fDataInCodeInfoLoadCommandForcedOff = true; fDataInCodeInfoLoadCommandForcedOn = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-data_in_code_info") == 0 ) { fDataInCodeInfoLoadCommandForcedOn = true; @@ -2915,22 +3332,26 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_symbols_weak_list") == 0 ) { snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_weak_list", fForceWeakSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbols_not_weak_list") == 0 ) { snapshotFileArgIndex = 1; loadExportFile(argv[++i], "-force_symbols_not_weak_list", fForceNotWeakSymbols); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbol_weak") == 0 ) { const char* symbol = argv[++i]; if ( symbol == NULL ) throw "-force_symbol_weak missing "; fForceWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-force_symbol_not_weak") == 0 ) { const char* symbol = argv[++i]; if ( symbol == NULL ) throw "-force_symbol_not_weak missing "; fForceNotWeakSymbols.insert(symbol); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) { snapshotFileArgIndex = 1; @@ -2945,13 +3366,16 @@ void Options::parse(int argc, const char* argv[]) if ( strchr(envarg, '=') == NULL ) throw "-dyld_env missing ENV=VALUE"; fDyldEnvironExtras.push_back(envarg); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) { fPageAlignDataAtoms = true; + cannotBeUsedWithBitcode(arg); } else if (strcmp(arg, "-debug_snapshot") == 0) { fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); } else if (strcmp(arg, "-snapshot_dir") == 0) { const char* path = argv[++i]; @@ -2960,12 +3384,15 @@ void Options::parse(int argc, const char* argv[]) fLinkSnapshot.setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); fLinkSnapshot.setSnapshotPath(path); fSnapshotRequested = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-new_main") == 0 ) { fEntryPointLoadCommandForceOn = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_new_main") == 0 ) { fEntryPointLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-source_version") == 0 ) { const char* vers = argv[++i]; @@ -2978,6 +3405,7 @@ void Options::parse(int argc, const char* argv[]) } else if ( strcmp(arg, "-no_source_version") == 0 ) { fSourceVersionLoadCommandForceOff = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-sdk_version") == 0 ) { const char* vers = argv[++i]; @@ -2986,13 +3414,14 @@ void Options::parse(int argc, const char* argv[]) fSDKVersion = parseVersionNumber32(vers); } else if ( strcmp(arg, "-dependent_dr_info") == 0 ) { - fDependentDRInfoForcedOn = true; + warnObsolete(arg); } else if ( strcmp(arg, "-no_dependent_dr_info") == 0 ) { - fDependentDRInfoForcedOff = true; + warnObsolete(arg); } else if ( strcmp(arg, "-kexts_use_stubs") == 0 ) { fKextsUseStubs = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(argv[i], "-dependency_info") == 0 ) { snapshotArgCount = 0; @@ -3027,23 +3456,28 @@ void Options::parse(int argc, const char* argv[]) } } fLinkerOptions.push_back(opts); + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-allow_simulator_linking_to_macosx_dylibs") == 0 ) { fAllowSimulatorToLinkWithMacOSX = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-keep_dwarf_unwind") == 0 ) { fKeepDwarfUnwindForcedOn = true; fKeepDwarfUnwindForcedOff = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_keep_dwarf_unwind") == 0 ) { fKeepDwarfUnwindForcedOn = false; fKeepDwarfUnwindForcedOff = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-verbose_optimization_hints") == 0 ) { fVerboseOptimizationHints = true; } else if ( strcmp(arg, "-ignore_optimization_hints") == 0 ) { fIgnoreOptimizationHints = true; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-no_dtrace_dof") == 0 ) { fGenerateDtraceDOF = false; @@ -3053,30 +3487,35 @@ void Options::parse(int argc, const char* argv[]) throw "-rename_section missing
"; addSectionRename(argv[i+1], argv[i+2], argv[i+3], argv[i+4]); i += 4; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-rename_segment") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) throw "-rename_segment missing "; addSegmentRename(argv[i+1], argv[i+2]); i += 2; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-move_to_ro_segment") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) throw "-move_to_ro_segment missing "; addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesCode, "-move_to_ro_segment"); i += 2; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-move_to_rw_segment") == 0 ) { if ( (argv[i+1]==NULL) || (argv[i+2]==NULL) ) throw "-move_to_rw_segment missing "; addSymbolMove(argv[i+1], argv[i+2], fSymbolsMovesData, "-move_to_rw_segment"); i += 2; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-trace_symbol_layout") == 0 ) { fTraceSymbolLayout = true; } else if ( strcmp(arg, "-no_branch_islands") == 0 ) { fAllowBranchIslands = false; + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-segment_order") == 0 ) { // ex: -segment_order __TEXT:__DATA:__JUNK @@ -3099,6 +3538,7 @@ void Options::parse(int argc, const char* argv[]) break; } } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-section_order") == 0 ) { // ex: -section_order __DATA __data:__const:__nl_pointers @@ -3126,6 +3566,7 @@ void Options::parse(int argc, const char* argv[]) break; } } + cannotBeUsedWithBitcode(arg); } else if ( strcmp(arg, "-application_extension") == 0 ) { fMarkAppExtensionSafe = true; @@ -3144,6 +3585,25 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-force_load_swift_libs") == 0 ) { fForceLoadSwiftLibs = true; } + else if ( strcmp(arg, "-not_for_dyld_shared_cache") == 0 ) { + fSharedRegionEligibleForceOff = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-dirty_data_list") == 0 ) { + if ( argv[i+1] == NULL ) + throw "-dirty_data_list missing "; + addSymbolMove("__DATA_DIRTY", argv[i+1], fSymbolsMovesData, "-dirty_data_list"); + ++i; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-data_const") == 0 ) { + fUseDataConstSegmentForceOn = true; + cannotBeUsedWithBitcode(arg); + } + else if ( strcmp(arg, "-no_data_const") == 0 ) { + fUseDataConstSegmentForceOff = true; + cannotBeUsedWithBitcode(arg); + } // put this last so that it does not interfer with other options starting with 'i' else if ( strncmp(arg, "-i", 2) == 0 ) { const char* colon = strchr(arg, ':'); @@ -3291,6 +3751,9 @@ void Options::buildSearchPaths(int argc, const char* argv[]) throw "-dependency_info missing "; fDependencyInfoPath = path; } + else if ( strcmp(argv[i], "-bitcode_bundle") == 0 ) { + fBundleBitcode = true; + } } int standardLibraryPathsStartIndex = libraryPaths.size(); int standardFrameworkPathsStartIndex = frameworkPaths.size(); @@ -3544,21 +4007,20 @@ void Options::reconfigureDefaults() } // set default min OS version - if ( (fMacVersionMin == ld::macVersionUnset) - && (fIOSVersionMin == ld::iOSVersionUnset) ) { + if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fWatchOSVersionMin == ld::wOSVersionUnset) ) { // if neither -macosx_version_min nor -iphoneos_version_min used, try environment variables const char* macVers = getenv("MACOSX_DEPLOYMENT_TARGET"); const char* iPhoneVers = getenv("IPHONEOS_DEPLOYMENT_TARGET"); const char* iOSVers = getenv("IOS_DEPLOYMENT_TARGET"); - const char* iOSSimulatorVers = getenv("IOS_SIMULATOR_DEPLOYMENT_TARGET"); - if ( macVers != NULL ) + const char* wOSVers = getenv("WATCHOS_DEPLOYMENT_TARGET"); + if ( macVers != NULL ) setMacOSXVersionMin(macVers); else if ( iPhoneVers != NULL ) setIOSVersionMin(iPhoneVers); else if ( iOSVers != NULL ) setIOSVersionMin(iOSVers); - else if ( iOSSimulatorVers != NULL ) - setIOSVersionMin(iOSSimulatorVers); + else if ( wOSVers != NULL ) + setWatchOSVersionMin(wOSVers); else { // if still nothing, set default based on architecture switch ( fArchitecture ) { @@ -3570,7 +4032,7 @@ void Options::reconfigureDefaults() setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION); #else warning("-macosx_version_min not specified, assuming 10.6"); - fMacVersionMin = ld::mac10_6; + setMacOSXVersionMin("10.6"); #endif } break; @@ -3580,8 +4042,14 @@ void Options::reconfigureDefaults() warning("-ios_version_min not specified, assuming " DEFAULT_IPHONEOS_MIN_VERSION); setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION); #else - warning("-ios_version_min not specified, assuming 6.0"); - setIOSVersionMin("6.0"); + if ( fSubArchitecture == CPU_SUBTYPE_ARM_V7K ) { + warning("-watchos_version_min not specified, assuming 2.0"); + setWatchOSVersionMin("2.0"); + } + else { + warning("-ios_version_min not specified, assuming 6.0"); + setIOSVersionMin("6.0"); + } #endif } break; @@ -3596,19 +4064,19 @@ void Options::reconfigureDefaults() // adjust min based on architecture switch ( fArchitecture ) { case CPU_TYPE_I386: - if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) { + if ( (fPlatform == kPlatformOSX) && (fMacVersionMin < ld::mac10_4) ) { //warning("-macosx_version_min should be 10.4 or later for i386"); fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_X86_64: - if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) { + if ( (fPlatform == kPlatformOSX) && (fMacVersionMin < ld::mac10_4) ) { //warning("-macosx_version_min should be 10.4 or later for x86_64"); fMacVersionMin = ld::mac10_4; } break; case CPU_TYPE_ARM64: - if ( fIOSVersionMin < ld::iOS_7_0 ) { + if ( (fPlatform == kPlatformiOS) && (fIOSVersionMin < ld::iOS_7_0) ) { //warning("-mios_version_min should be 7.0 or later for arm64"); fIOSVersionMin = ld::iOS_7_0; } @@ -3661,12 +4129,12 @@ void Options::reconfigureDefaults() fUndefinedTreatment = kUndefinedDynamicLookup; break; case CPU_TYPE_ARM: - if ( fIOSVersionMin >= ld::iOS_5_0 ) { + if ( min_iOS(ld::iOS_5_0) ) { // iOS 5.0 and later use new MH_KEXT_BUNDLE type fMakeCompressedDyldInfo = false; fMakeCompressedDyldInfoForceOff = true; // kexts are PIC in iOS 6.0 and later - fAllowTextRelocs = (fIOSVersionMin < ld::iOS_6_0); + fAllowTextRelocs = !min_iOS(ld::iOS_6_0); fKextsUseStubs = !fAllowTextRelocs; fUndefinedTreatment = kUndefinedDynamicLookup; break; @@ -3841,7 +4309,7 @@ void Options::reconfigureDefaults() // determine if info for shared region should be added if ( fOutputKind == Options::kDynamicLibrary ) { if ( minOS(ld::mac10_5, ld::iOS_3_1) ) - if ( !fPrebind ) + if ( !fPrebind && !fSharedRegionEligibleForceOff ) if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0) || (strncmp(this->installPath(), "/System/Library/", 16) == 0) ) fSharedRegionEligible = true; @@ -3850,7 +4318,41 @@ void Options::reconfigureDefaults() // Enable dyld to be put into the dyld shared cache fSharedRegionEligible = true; } - + + // warn if -rpath is used with OS dylibs + if ( fSharedRegionEligible && !fRPaths.empty() ) + warning("-rpath cannot be used with dylibs that will be in the dyld shared cache"); + + // automatically use __DATA_CONST in iOS dylibs + if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0) && !fUseDataConstSegmentForceOff ) { + fUseDataConstSegment = true; + } + if ( fUseDataConstSegmentForceOn ) { + fUseDataConstSegment = true; + } + if ( fUseDataConstSegment ) { + addSectionRename("__DATA", "__got", "__DATA_CONST", "__got"); + addSectionRename("__DATA", "__la_symbol_ptr", "__DATA_CONST", "__la_symbol_ptr"); + addSectionRename("__DATA", "__nl_symbol_ptr", "__DATA_CONST", "__nl_symbol_ptr"); + addSectionRename("__DATA", "__const", "__DATA_CONST", "__const"); + addSectionRename("__DATA", "__cfstring", "__DATA_CONST", "__cfstring"); + addSectionRename("__DATA", "__mod_init_func", "__DATA_CONST", "__mod_init_func"); + addSectionRename("__DATA", "__mod_term_func", "__DATA_CONST", "__mod_term_func"); + addSectionRename("__DATA", "__objc_classlist", "__DATA_CONST", "__objc_classlist"); + addSectionRename("__DATA", "__objc_nlclslist", "__DATA_CONST", "__objc_nlclslist"); + addSectionRename("__DATA", "__objc_catlist", "__DATA_CONST", "__objc_catlist"); + addSectionRename("__DATA", "__objc_nlcatlist", "__DATA_CONST", "__objc_nlcatlist"); + addSectionRename("__DATA", "__objc_protolist", "__DATA_CONST", "__objc_protolist"); + addSectionRename("__DATA", "__objc_imageinfo", "__DATA_CONST", "__objc_imageinfo"); + addSectionRename("__DATA", "__objc_const", "__DATA_CONST", "__objc_const"); + } + + // Use V2 shared cache info when targetting newer OSs + if ( fSharedRegionEligible && minOS(ld::mac10_Future, ld::iOS_9_0)) { + fSharedRegionEncodingV2 = true; + fIgnoreOptimizationHints = true; + } + // figure out if module table is needed for compatibility with old ld/dyld if ( fOutputKind == Options::kDynamicLibrary ) { switch ( fArchitecture ) { @@ -3935,12 +4437,16 @@ void Options::reconfigureDefaults() case Options::kDynamicLibrary: case Options::kDynamicBundle: // Add LC_ENCRYPTION_INFO load command to bundled frameworks - if ( fIOSVersionMin < ld::iOS_8_0 ) + if ( !min_iOS(ld::iOS_7_0) ) fEncryptable = false; break; } if ( (fArchitecture != CPU_TYPE_ARM) && (fArchitecture != CPU_TYPE_ARM64) ) fEncryptable = false; + if ( fEncryptableForceOn ) + fEncryptable = true; + else if ( fEncryptableForceOff ) + fEncryptable = false; // don't move inits in dyld because dyld wants certain // entries point at stable locations at the start of __text @@ -4045,7 +4551,7 @@ void Options::reconfigureDefaults() if ( (fArchitecture == CPU_TYPE_ARM) && fArchSupportsThumb2 && (fOutputKind == kDynamicExecutable) - && (fIOSVersionMin >= ld::iOS_4_3) ) { + && min_iOS(ld::iOS_4_3) ) { fPositionIndependentExecutable = true; } @@ -4086,7 +4592,10 @@ void Options::reconfigureDefaults() if ( fMacVersionMin >= ld::mac10_7 ) { fTLVSupport = true; } - else if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= ld::iOS_8_0) ) { + else if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_8_0) ) { + fTLVSupport = true; + } + else if ( (fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_9_0) ) { fTLVSupport = true; } @@ -4208,35 +4717,7 @@ void Options::reconfigureDefaults() fSourceVersionLoadCommand = false; break; } - - - // add LC_DYLIB_CODE_SIGN_DRS - switch ( fOutputKind ) { - case Options::kDynamicExecutable: - case Options::kDynamicLibrary: - case Options::kDynamicBundle: - if ( fDependentDRInfoForcedOn ) { - fDependentDRInfo = true; - } - else if ( fDependentDRInfoForcedOff ) { - fDependentDRInfo = false; - } - else { - if ( minOS(ld::mac10_8, ld::iOS_6_0) ) - fDependentDRInfo = true; - else - fDependentDRInfo = false; - } - break; - case Options::kKextBundle: - case Options::kDyld: - case Options::kStaticExecutable: - case Options::kObjectFile: - case Options::kPreload: - fDependentDRInfo = false; - break; - } - + // if -sdk_version not on command line, infer from -syslibroot if ( (fSDKVersion == 0) && (fSDKPaths.size() > 0) ) { const char* sdkPath = fSDKPaths.front(); @@ -4275,9 +4756,7 @@ void Options::reconfigureDefaults() // allow trie based absolute symbols if targeting new enough OS if ( fMakeCompressedDyldInfo ) { if ( minOS(ld::mac10_9, ld::iOS_7_0) ) { - // Allow absolute symbols in export trie for device but not simulator - if ( !fTargetIOSSimulator ) - fAbsoluteSymbols = true; + fAbsoluteSymbols = true; } } @@ -4299,15 +4778,14 @@ void Options::reconfigureDefaults() case Options::kDynamicBundle: case Options::kDyld: if ( (fArchitecture == CPU_TYPE_ARM64) - || ((fArchitecture == CPU_TYPE_ARM) && (fIOSVersionMin >= ld::iOS_8_0) && - ((fSubArchitecture == CPU_SUBTYPE_ARM_V7S) || (fSubArchitecture == CPU_SUBTYPE_ARM_V7))) ) { + || ((fArchitecture == CPU_TYPE_ARM) && min_iOS(ld::iOS_7_0)) ) { fSegmentAlignment = 4096*4; } break; case Options::kStaticExecutable: case Options::kKextBundle: // 16KB segments for arm64 kexts - if ( (fArchitecture == CPU_TYPE_ARM64) && (fIOSVersionMin >= ld::iOS_9_0) ) { + if ( (fArchitecture == CPU_TYPE_ARM64) && min_iOS(ld::iOS_9_0) ) { fSegmentAlignment = 4096*4; } break; @@ -4353,6 +4831,21 @@ void Options::reconfigureDefaults() fBaseAddress = alignedBaseAddress; } + // If -dirty_data_list not specified, look in $SDKROOT/AppleInternal/DirtyDataFiles/.dirty for dirty data list + if ( fSymbolsMovesData.empty() && fUseDataConstSegment && ( fDylibInstallName != NULL) && !fSDKPaths.empty() ) { + const char* dylibLeaf = strrchr(fDylibInstallName, '/'); + if ( dylibLeaf ) { + char path[PATH_MAX]; + strlcpy(path , fSDKPaths.front(), sizeof(path)); + strlcat(path , "/AppleInternal/DirtyDataFiles", sizeof(path)); + strlcat(path , dylibLeaf, sizeof(path)); + strlcat(path , ".dirty", sizeof(path)); + FileInfo info; + if ( info.checkFileExists(*this, path) ) + addSymbolMove("__DATA_DIRTY", path, fSymbolsMovesData, "-dirty_data_list"); + } + } + } void Options::checkIllegalOptionCombinations() @@ -4798,8 +5291,9 @@ void Options::checkIllegalOptionCombinations() throw "-segment_order can only used used with -preload output"; // warn if building an embedded iOS dylib for pre-iOS 8 + // How can we suppress "ld: warning: embedded dylibs/frameworks only run on iOS 8 or laterÓ when building XCTest? if ( (fOutputKind == Options::kDynamicLibrary) && (fIOSVersionMin != ld::iOSVersionUnset) && (fDylibInstallName != NULL) ) { - if ( (fIOSVersionMin < ld::iOS_8_0) && (fDylibInstallName[0] == '@') ) + if ( !min_iOS(ld::iOS_8_0) && (fDylibInstallName[0] == '@') && !fEncryptableForceOff ) warning("embedded dylibs/frameworks only run on iOS 8 or later"); } } @@ -4937,14 +5431,28 @@ const char* Options::demangleSymbol(const char* sym) const if ( !fDemangle ) return sym; + static size_t size = 1024; + static char* buff = (char*)malloc(size); + +#if DEMANGLE_SWIFT + // only try to demangle symbols that look like Swift symbols + if ( strncmp(sym, "__T", 3) == 0 ) { + size_t demangledSize = fnd_get_demangled_name(&sym[1], buff, size); + if ( demangledSize > size ) { + size = demangledSize+2; + buff = (char*)realloc(buff, size); + demangledSize = fnd_get_demangled_name(&sym[1], buff, size); + } + if ( demangledSize != 0 ) + return buff; + } +#endif + // only try to demangle symbols that look like C++ symbols if ( strncmp(sym, "__Z", 3) != 0 ) return sym; - static size_t size = 1024; - static char* buff = (char*)malloc(size); int status; - char* result = abi::__cxa_demangle(&sym[1], buff, &size, &status); if ( result != NULL ) { // if demangling successful, keep buffer for next demangle diff --git a/src/ld/Options.h b/src/ld/Options.h index 39ee6fe..bc70e7f 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -35,6 +35,7 @@ #include "ld.hpp" #include "Snapshot.h" +#include "MachOFileAbstraction.hpp" extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); @@ -86,6 +87,46 @@ public: enum UUIDMode { kUUIDNone, kUUIDRandom, kUUIDContent }; enum LocalSymbolHandling { kLocalSymbolsAll, kLocalSymbolsNone, kLocalSymbolsSelectiveInclude, kLocalSymbolsSelectiveExclude }; enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; +#if SUPPORT_APPLE_TV + enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS, kPlatform_tvOS }; +#else + enum Platform { kPlatformUnknown, kPlatformOSX, kPlatformiOS, kPlatformWatchOS }; +#endif + + static Platform platformForLoadCommand(uint32_t lc) { + switch (lc) { + case LC_VERSION_MIN_MACOSX: + return kPlatformOSX; + case LC_VERSION_MIN_IPHONEOS: + return kPlatformiOS; + case LC_VERSION_MIN_WATCHOS: + return kPlatformWatchOS; + #if SUPPORT_APPLE_TV + case LC_VERSION_MIN_TVOS: + return kPlatform_tvOS; + #endif + } + assert(!lc && "unknown LC_VERSION_MIN load command"); + return kPlatformUnknown; + } + + static const char* platformName(Platform platform) { + switch (platform) { + case kPlatformOSX: + return "OSX"; + case kPlatformiOS: + return "iOS"; + case kPlatformWatchOS: + return "watchOS"; + #if SUPPORT_APPLE_TV + case kPlatform_tvOS: + return "tvOS"; + #endif + case kPlatformUnknown: + default: + return "(unknown)"; + } + } class FileInfo { public: @@ -208,7 +249,7 @@ public: bool allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; } bool forceCpuSubtypeAll() const { return fForceSubtypeAll; } const char* architectureName() const { return fArchitectureName; } - void setArchitecture(cpu_type_t, cpu_subtype_t subtype); + void setArchitecture(cpu_type_t, cpu_subtype_t subtype, Options::Platform platform); bool archSupportsThumb2() const { return fArchSupportsThumb2; } OutputKind outputKind() const { return fOutputKind; } bool prebind() const { return fPrebind; } @@ -233,6 +274,7 @@ public: bool allGlobalsAreDeadStripRoots() const; bool shouldExport(const char*) const; bool shouldReExport(const char*) const; + std::vector exportsData() const; bool ignoreOtherArchInputFiles() const { return fIgnoreOtherArchFiles; } bool traceDylibs() const { return fTraceDylibs; } bool traceArchives() const { return fTraceArchives; } @@ -240,7 +282,10 @@ public: UndefinedTreatment undefinedTreatment() const { return fUndefinedTreatment; } ld::MacVersionMin macosxVersionMin() const { return fMacVersionMin; } ld::IOSVersionMin iOSVersionMin() const { return fIOSVersionMin; } + ld::WatchOSVersionMin watchOSVersionMin() const { return fWatchOSVersionMin; } + uint32_t minOSversion() const; bool minOS(ld::MacVersionMin mac, ld::IOSVersionMin iPhoneOS); + bool min_iOS(ld::IOSVersionMin requirediOSMin); bool messagesPrefixedWithArchitecture(); Treatment picTreatment(); WeakReferenceMismatchTreatment weakReferenceMismatchTreatment() const { return fWeakReferenceMismatchTreatment; } @@ -266,7 +311,7 @@ public: CommonsMode commonsMode() const { return fCommonsMode; } bool warnCommons() const { return fWarnCommons; } bool keepRelocations(); - FileInfo findFile(const char* path) const; + FileInfo findFile(const std::string &path) const; UUIDMode UUIDMode() const { return fUUIDMode; } bool warnStabs(); bool pauseAtEnd() { return fPause; } @@ -296,7 +341,7 @@ public: const std::vector& dylibOverrides() const { return fDylibOverrides; } const char* generatedMapPath() const { return fMapPath; } bool positionIndependentExecutable() const { return fPositionIndependentExecutable; } - Options::FileInfo findFileUsingPaths(const char* path) const; + Options::FileInfo findFileUsingPaths(const std::string &path) const; bool deadStripDylibs() const { return fDeadStripDylibs; } bool allowedUndefined(const char* name) const { return ( fAllowedUndefined.find(name) != fAllowedUndefined.end() ); } bool someAllowedUndefines() const { return (fAllowedUndefined.size() != 0); } @@ -311,6 +356,7 @@ public: bool needsUnwindInfoSection() const { return fAddCompactUnwindEncoding; } const std::vector& llvmOptions() const{ return fLLVMOptions; } const std::vector& segmentOrder() const{ return fSegmentOrder; } + bool segmentOrderAfterFixedAddressSegment(const char* segName) const; const std::vector* sectionOrder(const char* segName) const; const std::vector& dyldEnvironExtras() const{ return fDyldEnvironExtras; } const std::vector& astFilePaths() const{ return fASTFilePaths; } @@ -346,7 +392,7 @@ public: bool objcGc() const { return fObjCGc; } bool objcGcOnly() const { return fObjCGcOnly; } bool canUseThreadLocalVariables() const { return fTLVSupport; } - bool addVersionLoadCommand() const { return fVersionLoadCommand; } + bool addVersionLoadCommand() const { return fVersionLoadCommand && (fPlatform != kPlatformUnknown); } bool addFunctionStarts() const { return fFunctionStartsLoadCommand; } bool addDataInCodeInfo() const { return fDataInCodeInfoLoadCommand; } bool canReExportSymbols() const { return fCanReExportSymbols; } @@ -364,6 +410,15 @@ public: bool markAppExtensionSafe() const { return fMarkAppExtensionSafe; } bool checkDylibsAreAppExtensionSafe() const { return fCheckAppExtensionSafe; } bool forceLoadSwiftLibs() const { return fForceLoadSwiftLibs; } + bool bundleBitcode() const { return fBundleBitcode; } + bool hideSymbols() const { return fHideSymbols; } + bool renameReverseSymbolMap() const { return fReverseMapUUIDRename; } + const char* reverseSymbolMapPath() const { return fReverseMapPath; } + std::string reverseMapTempPath() const { return fReverseMapTempPath; } + bool ltoCodegenOnly() const { return fLTOCodegenOnly; } + bool ignoreAutoLink() const { return fIgnoreAutoLink; } + bool sharedRegionEncodingV2() const { return fSharedRegionEncodingV2; } + bool useDataConstSegment() const { return fUseDataConstSegment; } bool hasWeakBitTweaks() const; bool forceWeak(const char* symbolName) const; bool forceNotWeak(const char* symbolName) const; @@ -375,7 +430,6 @@ public: bool needsThreadLoadCommand() const { return fNeedsThreadLoadCommand; } bool needsEntryPointLoadCommand() const { return fEntryPointLoadCommand; } bool needsSourceVersionLoadCommand() const { return fSourceVersionLoadCommand; } - bool needsDependentDRInfo() const { return fDependentDRInfo; } bool canUseAbsoluteSymbols() const { return fAbsoluteSymbols; } bool allowSimulatorToLinkWithMacOSX() const { return fAllowSimulatorToLinkWithMacOSX; } uint64_t sourceVersion() const { return fSourceVersion; } @@ -395,6 +449,11 @@ public: const std::vector& segmentRenames() const { return fSegmentRenames; } bool moveRoSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const; bool moveRwSymbol(const char* symName, const char* filePath, const char*& seg, bool& wildCardMatch) const; + Platform platform() const { return fPlatform; } + const std::vector& sdkPaths() const { return fSDKPaths; } + std::vector writeBitcodeLinkOptions() const; + std::string getSDKVersionStr() const; + std::string getPlatformStr() const; private: typedef std::unordered_map NameToOrder; @@ -414,6 +473,7 @@ private: NameSet::iterator regularBegin() const { return fRegular.begin(); } NameSet::iterator regularEnd() const { return fRegular.end(); } void remove(const NameSet&); + std::vector data() const; private: static bool hasWildCards(const char*); bool wildCardMatch(const char* pattern, const char* candidate) const; @@ -437,6 +497,8 @@ private: FileInfo& result) const; uint64_t parseVersionNumber64(const char*); uint32_t parseVersionNumber32(const char*); + std::string getVersionString32(uint32_t ver) const; + std::string getVersionString64(uint64_t ver) const; void parseSectionOrderFile(const char* segment, const char* section, const char* path); void parseOrderFile(const char* path, bool cstring); void addSection(const char* segment, const char* section, const char* path); @@ -450,6 +512,7 @@ private: void setUndefinedTreatment(const char* treatment); void setMacOSXVersionMin(const char* version); void setIOSVersionMin(const char* version); + void setWatchOSVersionMin(const char* version); void setWeakReferenceMismatchTreatment(const char* treatment); void addDylibOverride(const char* paths); void addSectionAlignment(const char* segment, const char* section, const char* alignment); @@ -465,6 +528,7 @@ private: void addSectionRename(const char* srcSegment, const char* srcSection, const char* dstSegment, const char* dstSection); void addSegmentRename(const char* srcSegment, const char* dstSegment); void addSymbolMove(const char* dstSegment, const char* symbolList, std::vector& list, const char* optionName); + void cannotBeUsedWithBitcode(const char* arg); // ObjectFile::ReaderOptions fReaderOptions; @@ -550,6 +614,7 @@ private: bool fStatistics; bool fPrintOptions; bool fSharedRegionEligible; + bool fSharedRegionEligibleForceOff; bool fPrintOrderFileStatistics; bool fReadOnlyx86Stubs; bool fPositionIndependentExecutable; @@ -562,6 +627,8 @@ private: bool fKextsUseStubs; bool fUsingLazyDylibLinking; bool fEncryptable; + bool fEncryptableForceOn; + bool fEncryptableForceOff; bool fOrderData; bool fMarkDeadStrippableDylib; bool fMakeCompressedDyldInfo; @@ -621,9 +688,6 @@ private: bool fSourceVersionLoadCommand; bool fSourceVersionLoadCommandForceOn; bool fSourceVersionLoadCommandForceOff; - bool fDependentDRInfo; - bool fDependentDRInfoForcedOn; - bool fDependentDRInfoForcedOff; bool fTargetIOSSimulator; bool fExportDynamic; bool fAbsoluteSymbols; @@ -639,14 +703,27 @@ private: bool fMarkAppExtensionSafe; bool fCheckAppExtensionSafe; bool fForceLoadSwiftLibs; + bool fSharedRegionEncodingV2; + bool fUseDataConstSegment; + bool fUseDataConstSegmentForceOn; + bool fUseDataConstSegmentForceOff; + bool fBundleBitcode; + bool fHideSymbols; + bool fReverseMapUUIDRename; + const char* fReverseMapPath; + std::string fReverseMapTempPath; + bool fLTOCodegenOnly; + bool fIgnoreAutoLink; + Platform fPlatform; DebugInfoStripping fDebugInfoStripping; const char* fTraceOutputFile; ld::MacVersionMin fMacVersionMin; ld::IOSVersionMin fIOSVersionMin; + ld::WatchOSVersionMin fWatchOSVersionMin; std::vector fAliases; std::vector fInitialUndefines; NameSet fAllowedUndefined; - NameSet fWhyLive; + SetWithWildcards fWhyLive; std::vector fExtraSections; std::vector fSectionAlignments; std::vector fOrderedSymbols; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index 1680ade..dd0e5b2 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -65,7 +66,6 @@ #include "LinkEdit.hpp" #include "LinkEditClassic.hpp" - namespace ld { namespace tool { @@ -82,7 +82,7 @@ OutputFile::OutputFile(const Options& opts) rebaseSection(NULL), bindingSection(NULL), weakBindingSection(NULL), lazyBindingSection(NULL), exportSection(NULL), splitSegInfoSection(NULL), functionStartsSection(NULL), - dataInCodeSection(NULL), optimizationHintsSection(NULL), dependentDRsSection(NULL), + dataInCodeSection(NULL), optimizationHintsSection(NULL), symbolTableSection(NULL), stringPoolSection(NULL), localRelocationsSection(NULL), externalRelocationsSection(NULL), sectionRelocationsSection(NULL), @@ -94,7 +94,6 @@ OutputFile::OutputFile(const Options& opts) _hasSplitSegInfo(opts.sharedRegionEligible()), _hasFunctionStartsInfo(opts.addFunctionStarts()), _hasDataInCodeInfo(opts.addDataInCodeInfo()), - _hasDependentDRInfo(opts.needsDependentDRInfo()), _hasDynamicSymbolTable(true), _hasLocalRelocations(!opts.makeCompressedDyldInfo()), _hasExternalRelocations(!opts.makeCompressedDyldInfo()), @@ -120,7 +119,6 @@ OutputFile::OutputFile(const Options& opts) _splitSegInfoAtom(NULL), _functionStartsAtom(NULL), _dataInCodeAtom(NULL), - _dependentDRInfoAtom(NULL), _optimizationHintsAtom(NULL) { } @@ -156,7 +154,10 @@ void OutputFile::write(ld::Internal& state) this->synthesizeDebugNotes(state); this->buildSymbolTable(state); this->generateLinkEditInfo(state); - this->makeSplitSegInfo(state); + if ( _options.sharedRegionEncodingV2() ) + this->makeSplitSegInfoV2(state); + else + this->makeSplitSegInfo(state); this->updateLINKEDITAddresses(state); //this->dumpAtomsBySection(state, false); this->writeOutputFile(state); @@ -267,13 +268,7 @@ void OutputFile::updateLINKEDITAddresses(ld::Internal& state) _optimizationHintsAtom->encode(); } - if ( _options.needsDependentDRInfo() ) { - // build dependent dylib DR info - assert(_dependentDRInfoAtom != NULL); - _dependentDRInfoAtom->encode(); - } - - // build classic symbol table + // build classic symbol table assert(_symbolTableAtom != NULL); _symbolTableAtom->encode(); assert(_indirectSymbolTableAtom != NULL); @@ -1233,6 +1228,8 @@ static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: return true; case ld::Fixup::kindSetTargetAddress: f = fixup; @@ -1244,6 +1241,8 @@ static bool isPageKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreARM64GOTLoadPage21: case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21: return true; default: break; @@ -1265,6 +1264,8 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: return true; case ld::Fixup::kindSetTargetAddress: f = fixup; @@ -1276,6 +1277,8 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) return !mustBeGOT; case ld::Fixup::kindStoreARM64GOTLoadPageOff12: case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: return true; default: break; @@ -1295,7 +1298,6 @@ static bool isPageOffsetKind(const ld::Fixup* fixup, bool mustBeGOT=false) break; \ } - void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld::Atom* atom, uint8_t* buffer) { //fprintf(stderr, "applyFixUps() on %s\n", atom->name()); @@ -2098,10 +2100,25 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta3 << 2), &infoC); if ( alt.info.count > 2 ) setInfo(state, atom, buffer, usedByHints, fit->offsetInAtom, (alt.info.delta4 << 2), &infoD); - + + if ( _options.sharedRegionEligible() ) { + if ( _options.sharedRegionEncodingV2() ) { + // In v2 format, all references might be move at dyld shared cache creation time + usableSegment = false; + } + else { + // In v1 format, only references to something in __TEXT segment could be optimized + usableSegment = (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0); + } + } + else { + // main executables can optimize any reference + usableSegment = true; + } + switch ( alt.info.kind ) { case LOH_ARM64_ADRP_ADRP: - // processed in pass 2 beacuse some ADRP may have been removed + // processed in pass 2 because some ADRP may have been removed break; case LOH_ARM64_ADRP_LDR: LOH_ASSERT(alt.info.count == 1); @@ -2109,10 +2126,12 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(isPageOffsetKind(infoB.fixup)); LOH_ASSERT(infoA.target == infoB.target); LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); isADRP = parseADRP(infoA.instruction, adrpInfoA); LOH_ASSERT(isADRP); isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); + // silently ignore LDRs transformed to ADD by TLV pass + if ( !isLDR && infoB.fixup->kind == ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12 ) + break; LOH_ASSERT(isLDR); LOH_ASSERT(ldrInfoB.baseReg == adrpInfoA.destReg); LOH_ASSERT(ldrInfoB.offset == (infoA.targetAddress & 0x00000FFF)); @@ -2122,7 +2141,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: set32LE(infoA.instructionContent, makeNOP()); set32LE(infoB.instructionContent, makeLDR_literal(ldrInfoB, infoA.targetAddress, infoB.instructionAddress)); if ( _options.verboseOptimizationHints() ) - fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal\n", infoB.instructionAddress); + fprintf(stderr, "adrp-ldr at 0x%08llX transformed to LDR literal, usableSegment=%d usableSegment\n", infoB.instructionAddress, usableSegment); } else { if ( _options.verboseOptimizationHints() ) @@ -2137,7 +2156,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(infoC.fixup == NULL); LOH_ASSERT(infoA.target == infoB.target); LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); isADRP = parseADRP(infoA.instruction, adrpInfoA); LOH_ASSERT(isADRP); isADD = parseADD(infoB.instruction, addInfoB); @@ -2193,7 +2211,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: isADD = parseADD(infoB.instruction, addInfoB); LOH_ASSERT(isADD); LOH_ASSERT(adrpInfoA.destReg == addInfoB.srcReg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); if ( usableSegment && withinOneMeg(infoA.targetAddress, infoA.instructionAddress) ) { // can do T4 transformation and use ADR set32LE(infoA.instructionContent, makeADR(addInfoB.destReg, infoA.targetAddress, infoA.instructionAddress)); @@ -2227,7 +2244,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(!ldrInfoB.isFloat); LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); //fprintf(stderr, "infoA.target=%p, %s, infoA.targetAddress=0x%08llX\n", infoA.target, infoA.target->name(), infoA.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { // can do T5 transform @@ -2246,7 +2262,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); if ( usableSegment && literalableSize && targetFourByteAligned && withinOneMeg(infoC.instructionAddress, infoA.targetAddress) ) { @@ -2295,7 +2310,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(infoC.fixup == NULL); LOH_ASSERT(infoA.target == infoB.target); LOH_ASSERT(infoA.targetAddress == infoB.targetAddress); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); isADRP = parseADRP(infoA.instruction, adrpInfoA); LOH_ASSERT(isADRP); isADD = parseADD(infoB.instruction, addInfoB); @@ -2348,7 +2362,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: LOH_ASSERT(ldrInfoB.size == 8); LOH_ASSERT(!ldrInfoB.isFloat); LOH_ASSERT(ldrInfoC.baseReg == ldrInfoB.reg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); if ( usableSegment && targetFourByteAligned && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { // can do T5 transform @@ -2367,7 +2380,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // target of GOT is in same linkage unit and B instruction was changed to ADD to compute LEA of target LOH_ASSERT(addInfoB.srcReg == adrpInfoA.destReg); LOH_ASSERT(addInfoB.destReg == ldrInfoC.baseReg); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); targetFourByteAligned = ( ((infoA.targetAddress) & 0x3) == 0 ); literalableSize = ( (ldrInfoC.size != 1) && (ldrInfoC.size != 2) ); if ( usableSegment && withinOneMeg(infoA.instructionAddress, infoA.targetAddress) ) { @@ -2410,7 +2422,6 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: isADRP = parseADRP(infoA.instruction, adrpInfoA); isADD = parseADD(infoB.instruction, addInfoB); isLDR = parseLoadOrStore(infoB.instruction, ldrInfoB); - usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) ); if ( isADRP ) { if ( isLDR ) { if ( usableSegment && withinOneMeg(infoB.instructionAddress, infoA.targetAddress) ) { @@ -2599,12 +2610,32 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) } } - void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) { const bool log = false; if ( (_options.outputKind() != Options::kObjectFile) || state.someObjectFileHasDwarf ) { uint8_t digest[CC_MD5_DIGEST_LENGTH]; + std::vector> excludeRegions; + uint64_t bitcodeCmdOffset; + uint64_t bitcodeCmdEnd; + uint64_t bitcodeSectOffset; + uint64_t bitcodePaddingEnd; + if ( _headersAndLoadCommandAtom->bitcodeBundleCommand(bitcodeCmdOffset, bitcodeCmdEnd, + bitcodeSectOffset, bitcodePaddingEnd) ) { + // Exclude embedded bitcode bundle section which contains timestamps in XAR header + // Note the timestamp is in the compressed XML header which means it might change the size of + // bitcode section. The load command which include the size of the section and the padding after + // the bitcode section should also be excluded in the UUID computation. + // Bitcode section should appears before LINKEDIT + // Exclude section cmd + if ( log ) fprintf(stderr, "bundle cmd start=0x%08llX, bundle cmd end=0x%08llX\n", + bitcodeCmdOffset, bitcodeCmdEnd); + excludeRegions.emplace_back(std::pair(bitcodeCmdOffset, bitcodeCmdEnd)); + // Exclude section content + if ( log ) fprintf(stderr, "bundle start=0x%08llX, bundle end=0x%08llX\n", + bitcodeSectOffset, bitcodePaddingEnd); + excludeRegions.emplace_back(std::pair(bitcodeSectOffset, bitcodePaddingEnd)); + } uint32_t stabsStringsOffsetStart; uint32_t tabsStringsOffsetEnd; uint32_t stabsOffsetStart; @@ -2631,20 +2662,30 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) if ( log ) fprintf(stderr, "firstStabStringFileOffset=0x%08llX\n", firstStabStringFileOffset); if ( log ) fprintf(stderr, "lastStabStringFileOffset=0x%08llX\n", lastStabStringFileOffset); assert(firstStabNlistFileOffset <= firstStabStringFileOffset); - + excludeRegions.emplace_back(std::pair(firstStabNlistFileOffset, lastStabNlistFileOffset)); + excludeRegions.emplace_back(std::pair(firstStabStringFileOffset, lastStabStringFileOffset)); + } + if ( !excludeRegions.empty() ) { CC_MD5_CTX md5state; CC_MD5_Init(&md5state); - // checksum everything up to first stabs nlist - if ( log ) fprintf(stderr, "checksum 0x%08X -> 0x%08llX\n", 0, firstStabNlistFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[0], firstStabNlistFileOffset); - // checkusm everything after last stabs nlist and up to first stabs string - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabNlistFileOffset, firstStabStringFileOffset); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabNlistFileOffset], firstStabStringFileOffset-lastStabNlistFileOffset); - // checksum everything after last stabs string to end of file - if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", lastStabStringFileOffset, _fileSize); - CC_MD5_Update(&md5state, &wholeBuffer[lastStabStringFileOffset], _fileSize-lastStabStringFileOffset); + // rdar://problem/19487042 include the output leaf file name in the hash + const char* lastSlash = strrchr(_options.outputFilePath(), '/'); + if ( lastSlash != NULL ) { + CC_MD5_Update(&md5state, lastSlash, strlen(lastSlash)); + } + uint64_t checksumStart = 0; + for ( auto& region : excludeRegions ) { + uint64_t regionStart = region.first; + uint64_t regionEnd = region.second; + assert(checksumStart <= regionStart && regionStart <= regionEnd && "Region overlapped"); + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", checksumStart, regionStart); + CC_MD5_Update(&md5state, &wholeBuffer[checksumStart], regionStart - checksumStart); + checksumStart = regionEnd; + } + if ( log ) fprintf(stderr, "checksum 0x%08llX -> 0x%08llX\n", checksumStart, _fileSize); + CC_MD5_Update(&md5state, &wholeBuffer[checksumStart], _fileSize-checksumStart); CC_MD5_Final(digest, &md5state); - if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], + if ( log ) fprintf(stderr, "uuid=%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7]); } else { @@ -2658,7 +2699,18 @@ void OutputFile::computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer) _headersAndLoadCommandAtom->recopyUUIDCommand(); } } - + +static int sDescriptorOfPathToRemove = -1; +static void removePathAndExit(int sig) +{ + if ( sDescriptorOfPathToRemove != -1 ) { + char path[MAXPATHLEN]; + if ( ::fcntl(sDescriptorOfPathToRemove, F_GETPATH, path) == 0 ) + ::unlink(path); + } + fprintf(stderr, "ld: interrupted\n"); + exit(1); +} void OutputFile::writeOutputFile(ld::Internal& state) { @@ -2724,12 +2776,16 @@ void OutputFile::writeOutputFile(ld::Internal& state) char tmpOutput[PATH_MAX]; uint8_t *wholeBuffer; if ( outputIsRegularFile && outputIsMappableFile ) { + // ld64 should clean up temporary files on SIGINT + ::signal(SIGINT, removePathAndExit); + strcpy(tmpOutput, _options.outputFilePath()); // If the path is too long to add a suffix for a temporary name then // just fall back to using the output path. if (strlen(tmpOutput)+strlen(filenameTemplate) < PATH_MAX) { strcat(tmpOutput, filenameTemplate); fd = mkstemp(tmpOutput); + sDescriptorOfPathToRemove = fd; } else { fd = open(tmpOutput, O_RDWR|O_CREAT, permissions); @@ -2788,11 +2844,24 @@ void OutputFile::writeOutputFile(ld::Internal& state) if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) { throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno); } + sDescriptorOfPathToRemove = -1; ::close(fd); // NFS: iOS incremental builds in Xcode 4.6 fail with codesign error // NFS seems to pad the end of the file sometimes. Calling trunc seems to correct it... ::truncate(_options.outputFilePath(), _fileSize); } + + // Rename symbol map file if needed + if ( _options.renameReverseSymbolMap() ) { + assert(_options.hideSymbols() && _options.reverseSymbolMapPath() != NULL && "Must hide symbol and specify a path"); + uuid_string_t UUIDString; + const uint8_t* rawUUID = _headersAndLoadCommandAtom->getUUID(); + uuid_unparse_upper(rawUUID, UUIDString); + char outputMapPath[PATH_MAX]; + sprintf(outputMapPath, "%s/%s.bcsymbolmap", _options.reverseSymbolMapPath(), UUIDString); + if ( ::rename(_options.reverseMapTempPath().c_str(), outputMapPath) != 0 ) + throwf("could not create bcsymbolmap file: %s", outputMapPath); + } } struct AtomByNameSorter @@ -3114,7 +3183,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3129,10 +3198,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -3176,7 +3241,7 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3191,10 +3256,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -3238,7 +3299,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + if ( _options.sharedRegionEncodingV2() ) + _splitSegInfoAtom = new SplitSegInfoV2Atom(_options, state, *this); + else + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3253,10 +3317,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -3300,7 +3360,10 @@ void OutputFile::addLinkEdit(ld::Internal& state) localRelocationsSection = state.addAtom(*_localRelocsAtom); } if ( _hasSplitSegInfo ) { - _splitSegInfoAtom = new SplitSegInfoAtom(_options, state, *this); + if ( _options.sharedRegionEncodingV2() ) + _splitSegInfoAtom = new SplitSegInfoV2Atom(_options, state, *this); + else + _splitSegInfoAtom = new SplitSegInfoV1Atom(_options, state, *this); splitSegInfoSection = state.addAtom(*_splitSegInfoAtom); } if ( _hasFunctionStartsInfo ) { @@ -3315,10 +3378,6 @@ void OutputFile::addLinkEdit(ld::Internal& state) _optimizationHintsAtom = new OptimizationHintsAtom(_options, state, *this); optimizationHintsSection = state.addAtom(*_optimizationHintsAtom); } - if ( _hasDependentDRInfo ) { - _dependentDRInfoAtom = new DependentDRAtom(_options, state, *this); - dependentDRsSection = state.addAtom(*_dependentDRInfoAtom); - } if ( _hasSymbolTable ) { _symbolTableAtom = new SymbolTableAtom(_options, state, *this); symbolTableSection = state.addAtom(*_symbolTableAtom); @@ -4401,7 +4460,6 @@ void OutputFile::addSectionRelocs(ld::Internal& state, ld::Internal::FinalSectio } - void OutputFile::makeSplitSegInfo(ld::Internal& state) { if ( !_options.sharedRegionEligible() ) @@ -4503,6 +4561,183 @@ void OutputFile::makeSplitSegInfo(ld::Internal& state) } } +void OutputFile::makeSplitSegInfoV2(ld::Internal& state) +{ + static const bool log = false; + if ( !_options.sharedRegionEligible() ) + return; + + for (std::vector::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) { + ld::Internal::FinalSection* sect = *sit; + if ( sect->isSectionHidden() ) + continue; + bool codeSection = (sect->type() == ld::Section::typeCode); + if (log) fprintf(stderr, "sect: %s, address=0x%llX\n", sect->sectionName(), sect->address); + for (std::vector::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { + const ld::Atom* atom = *ait; + const ld::Atom* target = NULL; + const ld::Atom* fromTarget = NULL; + uint32_t picBase = 0; + uint64_t accumulator = 0; + bool thumbTarget; + bool hadSubtract = false; + uint8_t fromSectionIndex = atom->machoSection(); + uint8_t toSectionIndex; + uint8_t kind = 0; + uint64_t fromOffset = 0; + uint64_t toOffset = 0; + uint64_t addend = 0; + for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + fromTarget = NULL; + kind = 0; + addend = 0; + toSectionIndex = 255; + fromOffset = atom->finalAddress() + fit->offsetInAtom - sect->address; + } + if ( this->setsTarget(fit->kind) ) { + accumulator = addressOf(state, fit, &target); + thumbTarget = targetIsThumb(state, fit); + if ( thumbTarget ) + accumulator |= 1; + toOffset = accumulator - state.atomToSection[target]->address; + if ( target->definition() != ld::Atom::definitionProxy ) { + if ( target->section().type() == ld::Section::typeMachHeader ) + toSectionIndex = 0; + else + toSectionIndex = target->machoSection(); + } + } + switch ( fit->kind ) { + case ld::Fixup::kindSubtractTargetAddress: + accumulator -= addressOf(state, fit, &fromTarget); + hadSubtract = true; + break; + case ld::Fixup::kindAddAddend: + accumulator += fit->u.addend; + addend = fit->u.addend; + break; + case ld::Fixup::kindSubtractAddend: + accumulator -= fit->u.addend; + picBase = fit->u.addend; + break; + case ld::Fixup::kindSetLazyOffset: + break; + case ld::Fixup::kindStoreBigEndian32: + case ld::Fixup::kindStoreLittleEndian32: + case ld::Fixup::kindStoreTargetAddressLittleEndian32: + if ( kind != DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 ) { + if ( hadSubtract ) + kind = DYLD_CACHE_ADJ_V2_DELTA_32; + else + kind = DYLD_CACHE_ADJ_V2_POINTER_32; + } + break; + case ld::Fixup::kindStoreLittleEndian64: + case ld::Fixup::kindStoreTargetAddressLittleEndian64: + if ( hadSubtract ) + kind = DYLD_CACHE_ADJ_V2_DELTA_64; + else + kind = DYLD_CACHE_ADJ_V2_POINTER_64; + break; + case ld::Fixup::kindStoreX86PCRel32: + case ld::Fixup::kindStoreX86PCRel32_1: + case ld::Fixup::kindStoreX86PCRel32_2: + case ld::Fixup::kindStoreX86PCRel32_4: + case ld::Fixup::kindStoreX86PCRel32GOTLoad: + case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreX86PCRel32GOT: + case ld::Fixup::kindStoreX86PCRel32TLVLoad: + case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad: + case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA: +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64PCRelToGOT: +#endif + if ( (fromSectionIndex != toSectionIndex) || !codeSection ) + kind = DYLD_CACHE_ADJ_V2_DELTA_32; + break; +#if SUPPORT_ARCH_arm64 + case ld::Fixup::kindStoreARM64Page21: + case ld::Fixup::kindStoreARM64GOTLoadPage21: + case ld::Fixup::kindStoreARM64GOTLeaPage21: + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64Page21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM64_ADRP; + break; + case ld::Fixup::kindStoreARM64PageOff12: + case ld::Fixup::kindStoreARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64PageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM64_OFF12; + break; + case ld::Fixup::kindStoreARM64Branch26: + case ld::Fixup::kindStoreTargetAddressARM64Branch26: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM64_BR26; + break; +#endif + case ld::Fixup::kindStoreARMHigh16: + case ld::Fixup::kindStoreARMLow16: + if ( (fromSectionIndex != toSectionIndex) && (fromTarget == atom) ) { + kind = DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT; + } + break; + case ld::Fixup::kindStoreARMBranch24: + case ld::Fixup::kindStoreTargetAddressARMBranch24: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_ARM_BR24; + break; + case ld::Fixup::kindStoreThumbLow16: + case ld::Fixup::kindStoreThumbHigh16: + if ( (fromSectionIndex != toSectionIndex) && (fromTarget == atom) ) { + kind = DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT; + } + break; + case ld::Fixup::kindStoreThumbBranch22: + case ld::Fixup::kindStoreTargetAddressThumbBranch22: + if ( fromSectionIndex != toSectionIndex ) + kind = DYLD_CACHE_ADJ_V2_THUMB_BR22; + break; + case ld::Fixup::kindSetTargetImageOffset: + kind = DYLD_CACHE_ADJ_V2_IMAGE_OFF_32; + accumulator = addressOf(state, fit, &target); + assert(target != NULL); + toSectionIndex = target->machoSection(); + toOffset = accumulator - state.atomToSection[target]->address; + hadSubtract = true; + break; + default: + break; + } + if ( fit->lastInCluster() ) { + if ( (kind != 0) && (target != NULL) && (target->definition() != ld::Atom::definitionProxy) ) { + if ( !hadSubtract && addend ) + toOffset += addend; + assert(toSectionIndex != 255); + if (log) fprintf(stderr, "from (%d.%s + 0x%llX) to (%d.%s + 0x%llX), kind=%d, atomAddr=0x%llX, sectAddr=0x%llx\n", + fromSectionIndex, sect->sectionName(), fromOffset, toSectionIndex, state.atomToSection[target]->sectionName(), + toOffset, kind, atom->finalAddress(), sect->address); + _splitSegV2Infos.push_back(SplitSegInfoV2Entry(fromSectionIndex, fromOffset, toSectionIndex, toOffset, kind)); + } + } + } + } + } +} + void OutputFile::writeMapFile(ld::Internal& state) { @@ -4618,7 +4853,6 @@ void OutputFile::writeMapFile(ld::Internal& state) } } - // used to sort atoms with debug notes class DebugNoteSorter { diff --git a/src/ld/OutputFile.h b/src/ld/OutputFile.h index 6bb793b..cbb1cfe 100644 --- a/src/ld/OutputFile.h +++ b/src/ld/OutputFile.h @@ -83,7 +83,6 @@ public: ld::Internal::FinalSection* functionStartsSection; ld::Internal::FinalSection* dataInCodeSection; ld::Internal::FinalSection* optimizationHintsSection; - ld::Internal::FinalSection* dependentDRsSection; ld::Internal::FinalSection* symbolTableSection; ld::Internal::FinalSection* stringPoolSection; ld::Internal::FinalSection* localRelocationsSection; @@ -132,12 +131,23 @@ public: }; struct SplitSegInfoEntry { - SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k, uint32_t e=0) : address(a), kind(k), extra(e) {} - uint64_t address; + SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k, uint32_t e=0) + : fixupAddress(a), kind(k), extra(e) {} + uint64_t fixupAddress; ld::Fixup::Kind kind; uint32_t extra; }; + struct SplitSegInfoV2Entry { + SplitSegInfoV2Entry(uint8_t fi, uint64_t fo, uint8_t ti, uint64_t to, uint8_t k) + : fixupSectionOffset(fo), targetSectionOffset(to), fixupSectionIndex(fi), targetSectionIndex(ti), referenceKind(k) {} + uint64_t fixupSectionOffset; + uint64_t targetSectionOffset; + uint8_t fixupSectionIndex; + uint8_t targetSectionIndex; + uint8_t referenceKind; + }; + private: void writeAtoms(ld::Internal& state, uint8_t* wholeBuffer); void computeContentUUID(ld::Internal& state, uint8_t* wholeBuffer); @@ -192,6 +202,7 @@ private: void makeSectionRelocations(ld::Internal& state); void makeDyldInfo(ld::Internal& state); void makeSplitSegInfo(ld::Internal& state); + void makeSplitSegInfoV2(ld::Internal& state); void writeMapFile(ld::Internal& state); uint64_t lookBackAddend(ld::Fixup::iterator fit); bool takesNoDiskSpace(const ld::Section* sect); @@ -271,7 +282,6 @@ private: const bool _hasSplitSegInfo; const bool _hasFunctionStartsInfo; const bool _hasDataInCodeInfo; - const bool _hasDependentDRInfo; bool _hasDynamicSymbolTable; bool _hasLocalRelocations; bool _hasExternalRelocations; @@ -296,6 +306,7 @@ public: std::vector _lazyBindingInfo; std::vector _weakBindingInfo; std::vector _splitSegInfos; + std::vector _splitSegV2Infos; class HeaderAndLoadCommandsAbtract* _headersAndLoadCommandAtom; class RelocationsAtomAbstract* _sectionsRelocationsAtom; class RelocationsAtomAbstract* _localRelocsAtom; @@ -311,7 +322,6 @@ public: class LinkEditAtom* _splitSegInfoAtom; class LinkEditAtom* _functionStartsAtom; class LinkEditAtom* _dataInCodeAtom; - class LinkEditAtom* _dependentDRInfoAtom; class LinkEditAtom* _optimizationHintsAtom; }; diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index 8a72b67..094c98d 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -53,6 +53,7 @@ #include "Options.h" #include "ld.hpp" +#include "Bitcode.hpp" #include "InputFiles.h" #include "SymbolTable.h" #include "Resolver.h" @@ -282,6 +283,8 @@ void Resolver::initializeState() _internal.objcObjectConstraint = ld::File::objcConstraintGC; _internal.cpuSubType = _options.subArchitecture(); + _internal.minOSVersion = _options.minOSversion(); + _internal.derivedPlatformLoadCommand = 0; // In -r mode, look for -linker_option additions if ( _options.outputKind() == Options::kObjectFile ) { @@ -330,14 +333,20 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons } } -static void userReadableSwiftVersion(uint8_t value, char versionString[32]) +static void userReadableSwiftVersion(uint8_t value, char versionString[64]) { switch (value) { case 1: strcpy(versionString, "1.0"); break; + case 2: + strcpy(versionString, "1.1"); + break; + case 3: + strcpy(versionString, "2.0"); + break; default: - sprintf(versionString, "0x%02X", value); + sprintf(versionString, "unknown ABI version 0x%02X", value); } } @@ -349,12 +358,67 @@ void Resolver::doFile(const ld::File& file) if ( objFile != NULL ) { // if file has linker options, process them ld::relocatable::File::LinkerOptionsList* lo = objFile->linkerOptions(); - if ( lo != NULL ) { + if ( lo != NULL && !_options.ignoreAutoLink() ) { for (relocatable::File::LinkerOptionsList::const_iterator it=lo->begin(); it != lo->end(); ++it) { this->doLinkerOption(*it, file.path()); } } - + + // Resolve bitcode section in the object file + if ( _options.bundleBitcode() ) { + if ( objFile->getBitcode() == NULL ) { + // No bitcode section, figure out if the object file comes from LTO/compiler static library + if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO && + objFile->sourceKind() != ld::relocatable::File::kSourceCompilerArchive ) { + switch ( _options.platform() ) { + case Options::kPlatformOSX: + case Options::kPlatformUnknown: + warning("all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. ", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + case Options::kPlatformiOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + break; + case Options::kPlatformWatchOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + warning("URGENT: all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor. " + "Note: This will be an error in the future.", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + #endif + } + } + } else { + // contains bitcode, check if it is just a marker + if ( objFile->getBitcode()->isMarker() ) { + // if the bitcode is just a marker, + // the executable will be created without bitcode section. + // Otherwise, create a marker. + if ( _options.outputKind() != Options::kDynamicExecutable && + _options.outputKind() != Options::kStaticExecutable ) + _internal.embedMarkerOnly = true; + // Issue a warning if the marker is in the static library and filesWithBitcode is not empty. + // That means there are object files actually compiled with full bitcode but the archive only has marker. + // Don't warn on normal object files because it can be a debug build using archives with full bitcode. + if ( !_internal.filesWithBitcode.empty() && objFile->sourceKind() == ld::relocatable::File::kSourceArchive ) + warning("full bitcode bundle could not be generated because '%s' was built only with bitcode marker. " + "The library must be generated from Xcode archive build with bitcode enabled (Xcode setting ENABLE_BITCODE)", objFile->path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + } else if ( !_internal.dropAllBitcode ) + _internal.filesWithBitcode.push_back(objFile); + } + } + // update which form of ObjC is being used switch ( file.objCConstraint() ) { case ld::File::objcConstraintNone: @@ -400,8 +464,8 @@ void Resolver::doFile(const ld::File& file) _internal.swiftVersion = file.swiftVersion(); } else if ( file.swiftVersion() != _internal.swiftVersion ) { - char fileVersion[32]; - char otherVersion[32]; + char fileVersion[64]; + char otherVersion[64]; userReadableSwiftVersion(file.swiftVersion(), fileVersion); userReadableSwiftVersion(_internal.swiftVersion, otherVersion); if ( file.swiftVersion() > _internal.swiftVersion ) { @@ -423,6 +487,18 @@ void Resolver::doFile(const ld::File& file) if ( ! objFile->canScatterAtoms() ) _internal.allObjectFilesScatterable = false; + // update minOSVersion off all .o files + uint32_t objMinOS = objFile->minOSVersion(); + if ( !objMinOS ) + _internal.objectFileFoundWithNoVersion = true; + + uint32_t objPlatformLC = objFile->platformLoadCommand(); + if ( (objPlatformLC != 0) && (_internal.derivedPlatformLoadCommand == 0) && (_options.outputKind() == Options::kObjectFile) ) + _internal.derivedPlatformLoadCommand = objPlatformLC; + + if ( (_options.outputKind() == Options::kObjectFile) && (objMinOS > _internal.minOSVersion) ) + _internal.minOSVersion = objMinOS; + // update cpu-sub-type cpu_subtype_t nextObjectSubType = file.cpuSubType(); switch ( _options.architecture() ) { @@ -464,6 +540,57 @@ void Resolver::doFile(const ld::File& file) } } if ( dylibFile != NULL ) { + // Check dylib for bitcode, if the library install path is relative path or @rpath, it has to contain bitcode + if ( _options.bundleBitcode() ) { + if ( dylibFile->getBitcode() == NULL && + dylibFile->installPath()[0] != '/' ) { + // Check if the dylib is from toolchain by checking the path + char tcLibPath[PATH_MAX]; + char ldPath[PATH_MAX]; + char tempPath[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + // toolchain library path should pointed to *.xctoolchain/usr/lib + if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( realpath(ldPath, tempPath) != NULL ) { + char* lastSlash = strrchr(tempPath, '/'); + if ( lastSlash != NULL ) + strcpy(lastSlash, "/../lib"); + } + } + // Compare toolchain library path to the dylib path + if ( realpath(tempPath, tcLibPath) == NULL || + realpath(dylibFile->path(), tempPath) == NULL || + strncmp(tcLibPath, tempPath, strlen(tcLibPath)) != 0 ) { + switch ( _options.platform() ) { + case Options::kPlatformOSX: + case Options::kPlatformUnknown: + warning("all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + case Options::kPlatformiOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target.", file.path()); + break; + case Options::kPlatformWatchOS: + throwf("'%s' does not contain bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor", file.path()); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + warning("URGENT: all bitcode will be dropped because '%s' was built without bitcode. " + "You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE) or obtain an updated library from the vendor. " + "Note: This will be an error in the future.", file.path()); + _internal.filesWithBitcode.clear(); + _internal.dropAllBitcode = true; + break; + #endif + } + } + } + } + // update which form of ObjC dylibs are being linked switch ( dylibFile->objCConstraint() ) { case ld::File::objcConstraintNone: @@ -509,18 +636,19 @@ void Resolver::doFile(const ld::File& file) if ( (depInstallName != NULL) && (depInstallName[0] != '/') ) { if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) { // only warn about linking against embedded dylib if it is built for iOS 8 or later - if ( dylibFile->iOSMinVersion() >= iOS_8_0 ) - warning("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); + if ( dylibFile->minOSVersion() >= iOS_8_0 ) + throwf("embedded dylibs/frameworks are only supported on iOS 8.0 and later (%s)", depInstallName); } } if ( _options.sharedRegionEligible() ) { assert(depInstallName != NULL); - if ( depInstallName[0] == '@' ) + if ( depInstallName[0] == '@' ) { warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache " - "cannot link with dylib that uses @rpath, @loaderpath, etc.", depInstallName, dylibFile->path()); - if ( (strncmp(depInstallName, "/usr/lib/", 9) != 0) && (strncmp(depInstallName, "/System/Library/", 16) != 0) ) + "cannot link with dylib that uses @rpath, @loader_path, etc.", depInstallName, dylibFile->path()); + } else if ( (strncmp(depInstallName, "/usr/lib/", 9) != 0) && (strncmp(depInstallName, "/System/Library/", 16) != 0) ) { warning("invalid -install_name (%s) in dependent dylib (%s). Dylibs/frameworks which might go in dyld shared cache " "cannot link with dylibs that won't be in the shared cache", depInstallName, dylibFile->path()); + } } } @@ -631,6 +759,8 @@ void Resolver::doAtom(const ld::Atom& atom) // add to set of dead-strip-roots, all symbols that the compiler marks as don't strip if ( atom.dontDeadStrip() ) _deadStripRoots.insert(&atom); + else if ( atom.dontDeadStripIfReferencesLive() ) + _dontDeadStripIfReferencesLive.push_back(&atom); if ( atom.scope() == ld::Atom::scopeGlobal ) { // -exported_symbols_list that has wildcards and -dead_strip @@ -883,6 +1013,8 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) case ld::Fixup::kindStoreTargetAddressARM64Page21: case ld::Fixup::kindStoreTargetAddressARM64GOTLoadPage21: case ld::Fixup::kindStoreTargetAddressARM64GOTLeaPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21: #endif if ( fit->binding == ld::Fixup::bindingByContentBound ) { // normally this was done in convertReferencesToIndirect() @@ -1015,6 +1147,38 @@ void Resolver::deadStripOptimize(bool force) this->markLive(**it, &rootChain); } + // special case atoms that need to be live if they reference something live + if ( ! _dontDeadStripIfReferencesLive.empty() ) { + for (std::vector::iterator it=_dontDeadStripIfReferencesLive.begin(); it != _dontDeadStripIfReferencesLive.end(); ++it) { + const Atom* liveIfRefLiveAtom = *it; + //fprintf(stderr, "live-if-live atom: %s\n", liveIfRefLiveAtom->name()); + if ( liveIfRefLiveAtom->live() ) + continue; + bool hasLiveRef = false; + for (ld::Fixup::iterator fit=liveIfRefLiveAtom->fixupsBegin(); fit != liveIfRefLiveAtom->fixupsEnd(); ++fit) { + const Atom* target = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = _internal.indirectBindingTable[fit->u.bindingIndex]; + break; + default: + break; + } + if ( (target != NULL) && target->live() ) + hasLiveRef = true; + } + if ( hasLiveRef ) { + WhyLiveBackChain rootChain; + rootChain.previous = NULL; + rootChain.referer = liveIfRefLiveAtom; + this->markLive(*liveIfRefLiveAtom, &rootChain); + } + } + } + // now remove all non-live atoms from _atoms const bool log = false; if ( log ) { @@ -1538,6 +1702,7 @@ void Resolver::linkTimeOptimize() optOpt.preserveAllGlobals = _options.allGlobalsAreDeadStripRoots() || _options.hasExportRestrictList(); optOpt.verbose = _options.verbose(); optOpt.saveTemps = _options.saveTempFiles(); + optOpt.ltoCodegenOnly = _options.ltoCodegenOnly(); optOpt.pie = _options.positionIndependentExecutable(); optOpt.mainExecutable = _options.linkingMainExecutable();; optOpt.staticExecutable = (_options.outputKind() == Options::kStaticExecutable); @@ -1548,8 +1713,12 @@ void Resolver::linkTimeOptimize() optOpt.keepDwarfUnwind = _options.keepDwarfUnwind(); optOpt.verboseOptimizationHints = _options.verboseOptimizationHints(); optOpt.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); + optOpt.simulator = _options.targetIOSSimulator(); + optOpt.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable)); + optOpt.bitcodeBundle = _options.bundleBitcode(); optOpt.arch = _options.architecture(); optOpt.mcpu = _options.mcpuLTO(); + optOpt.platform = _options.platform(); optOpt.llvmOptions = &_options.llvmOptions(); optOpt.initialUndefines = &_options.initialUndefines(); diff --git a/src/ld/Resolver.h b/src/ld/Resolver.h index 975772b..6631d11 100644 --- a/src/ld/Resolver.h +++ b/src/ld/Resolver.h @@ -126,6 +126,7 @@ private: ld::Internal& _internal; std::vector _atoms; std::set _deadStripRoots; + std::vector _dontDeadStripIfReferencesLive; std::vector _atomsWithUnresolvedReferences; std::vector _aliasesFromCmdLine; SymbolTable _symbolTable; diff --git a/src/ld/SymbolTable.cpp b/src/ld/SymbolTable.cpp index ea552f4..6014779 100644 --- a/src/ld/SymbolTable.cpp +++ b/src/ld/SymbolTable.cpp @@ -813,6 +813,15 @@ SymbolTable::IndirectBindingSlot SymbolTable::findSlotForReferences(const ld::At slot = _indirectBindingTable.size(); _pointerToCStringTable[atom] = slot; break; + case ld::Section::typeTLVPointers: + pos = _threadPointerTable.find(atom); + if ( pos != _threadPointerTable.end() ) { + *existingAtom = _indirectBindingTable[pos->second]; + return pos->second; + } + slot = _indirectBindingTable.size(); + _threadPointerTable[atom] = slot; + break; default: assert(0 && "section type does not support coalescing by references"); } diff --git a/src/ld/SymbolTable.h b/src/ld/SymbolTable.h index 14c7a9e..ce2f1dc 100644 --- a/src/ld/SymbolTable.h +++ b/src/ld/SymbolTable.h @@ -155,6 +155,7 @@ private: CStringToSlot _cstringTable; NameToMap _nonStdCStringSectionToMap; ReferencesToSlot _nonLazyPointerTable; + ReferencesToSlot _threadPointerTable; ReferencesToSlot _cfStringTable; ReferencesToSlot _objc2ClassRefTable; ReferencesToSlot _pointerToCStringTable; diff --git a/src/ld/code-sign-blobs/memutils.h b/src/ld/code-sign-blobs/memutils.h index 391ddc1..9e752f4 100644 --- a/src/ld/code-sign-blobs/memutils.h +++ b/src/ld/code-sign-blobs/memutils.h @@ -50,8 +50,11 @@ static const size_t systemAlignment = 4; // // Get the local alignment for a type, as used by the acting compiler. // -template -inline size_t alignof() { struct { char c; T t; } s; return sizeof(s) - sizeof(T); } +template +unsigned long myalignof() { + struct { char c; T t; } s; + return sizeof(s) - sizeof(T); +} // diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index 4ef1f14..0b5bc4a 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -79,6 +79,7 @@ extern "C" double log2 ( double ); #include "passes/branch_shim.h" #include "passes/objc.h" #include "passes/dylibs.h" +#include "passes/bitcode_bundle.h" #include "parsers/archive_file.h" #include "parsers/macho_relocatable_file.h" @@ -112,6 +113,8 @@ public: void setSectionSizesAndAlignments(); void sortSections(); void markAtomsOrdered() { _atomsOrderedInSections = true; } + bool hasReferenceToWeakExternal(const ld::Atom& atom); + virtual ~InternalState() {} private: @@ -137,6 +140,8 @@ private: static ld::Section _s_DATA_nl_symbol_ptr; static ld::Section _s_DATA_common; static ld::Section _s_DATA_zerofill; + static ld::Section _s_DATA_DIRTY_data; + static ld::Section _s_DATA_CONST_const; }; bool hasZeroForFileOffset(const ld::Section* sect); @@ -164,6 +169,9 @@ ld::Section InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld:: ld::Section InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); ld::Section InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill); ld::Section InternalState::FinalSection::_s_DATA_zerofill("__DATA", "__zerofill", ld::Section::typeZeroFill); +ld::Section InternalState::FinalSection::_s_DATA_DIRTY_data( "__DATA_DIRTY", "__data", ld::Section::typeUnclassified); +ld::Section InternalState::FinalSection::_s_DATA_CONST_const( "__DATA_CONST", "__const", ld::Section::typeUnclassified); + std::vector InternalState::FinalSection::_s_segmentsSeen; @@ -212,6 +220,14 @@ const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) return _s_TEXT_const; } + else if ( strcmp(sect.segmentName(), "__DATA_DIRTY") == 0 ) { + if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) + return _s_DATA_DIRTY_data; + } + else if ( strcmp(sect.segmentName(), "__DATA_CONST") == 0 ) { + if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) + return _s_DATA_CONST_const; + } break; case ld::Section::typeZeroFill: if ( mergeZeroFill ) @@ -304,7 +320,7 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint if ( sect.type() == ld::Section::typeLastSection ) return INT_MAX; const std::vector* sectionList = options.sectionOrder(sect.segmentName()); - if ( (options.outputKind() == Options::kPreload) && (sectionList != NULL) ) { + if ( ((options.outputKind() == Options::kPreload) || (options.outputKind() == Options::kDyld)) && (sectionList != NULL) ) { uint32_t count = 10; for (std::vector::const_iterator it=sectionList->begin(); it != sectionList->end(); ++it, ++count) { if ( strcmp(*it, sect.sectionName()) == 0 ) @@ -335,7 +351,7 @@ uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint return sectionsSeen+20; } } - else if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { + else if ( strncmp(sect.segmentName(), "__DATA", 6) == 0 ) { switch ( sect.type() ) { case ld::Section::typeLazyPointerClose: return 8; @@ -492,6 +508,34 @@ static void validateFixups(const ld::Atom& atom) } #endif +bool InternalState::hasReferenceToWeakExternal(const ld::Atom& atom) +{ + // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST + const ld::Atom* target = NULL; + for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { + if ( fit->firstInCluster() ) { + target = NULL; + } + switch ( fit->binding ) { + case ld::Fixup::bindingNone: + case ld::Fixup::bindingByNameUnbound: + break; + case ld::Fixup::bindingByContentBound: + case ld::Fixup::bindingDirectlyBound: + target = fit->u.target; + break; + case ld::Fixup::bindingsIndirectlyBound: + target = indirectBindingTable[fit->u.bindingIndex]; + break; + } + if ( (target != NULL) && (target->definition() == ld::Atom::definitionRegular) + && (target->combine() == ld::Atom::combineByName) && (target->scope() == ld::Atom::scopeGlobal) ) { + return true; + } + } + return false; +} + ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) { ld::Internal::FinalSection* fs = NULL; @@ -515,7 +559,8 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) if ( _options.moveRwSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { if ( (sectType != ld::Section::typeZeroFill) && (sectType != ld::Section::typeUnclassified) - && (sectType != ld::Section::typeTentativeDefs) ) { + && (sectType != ld::Section::typeTentativeDefs) + && (sectType != ld::Section::typeDyldInfo) ) { if ( !wildCardMatch ) warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType); } @@ -526,7 +571,8 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) } } if ( (fs == NULL) && _options.moveRoSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { - if ( atom.section().type() != ld::Section::typeCode ) { + if ( (sectType != ld::Section::typeCode) + && (sectType != ld::Section::typeUnclassified) ) { if ( !wildCardMatch ) warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not code (is %d)", atom.name(), path, dstSeg, sectType); } @@ -543,9 +589,24 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) const std::vector& segRenames = _options.segmentRenames(); for ( std::vector::const_iterator it=sectRenames.begin(); it != sectRenames.end(); ++it) { if ( (strcmp(sectName, it->fromSection) == 0) && (strcmp(atom.section().segmentName(), it->fromSegment) == 0) ) { - if ( _options.traceSymbolLayout() ) - printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), it->toSegment, it->toSection); - fs = this->getFinalSection(it->toSegment, it->toSection, sectType); + if ( _options.useDataConstSegment() && (strcmp(sectName, "__const") == 0) + && (strcmp(atom.section().segmentName(), "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST + fs = this->getFinalSection("__DATA", "__const_weak", sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/_const_weak\n", atom.name()); + } + else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) { + // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST + fs = this->getFinalSection("__DATA", "__got_weak", sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name()); + } + else { + fs = this->getFinalSection(it->toSegment, it->toSection, sectType); + if ( _options.traceSymbolLayout() ) + printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); + } } } if ( fs == NULL ) { @@ -558,8 +619,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) } } } - - + // if no override, use default location if ( fs == NULL ) { fs = this->getFinalSection(atom.section()); @@ -589,6 +649,7 @@ ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) // normal case fs->atoms.push_back(&atom); } + this->atomToSection[&atom] = fs; return fs; } @@ -731,7 +792,7 @@ void InternalState::setSectionSizesAndAlignments() bool pagePerAtom = false; uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; uint32_t atomModulus = atom->alignment().modulus; - if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) { + if ( _options.pageAlignDataAtoms() && ( strncmp(atom->section().segmentName(), "__DATA", 6) == 0) ) { // most objc sections cannot be padded bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 ); if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 ) @@ -811,13 +872,22 @@ uint64_t InternalState::assignFileOffsets() uint64_t address = 0; const char* lastSegName = ""; uint64_t floatingAddressStart = _options.baseAddress(); + bool haveFixedSegments = false; + // mark all sections as not having an address yet + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + sect->alignmentPaddingBytes = 0; + sect->address = ULLONG_MAX; + } + // first pass, assign addresses to sections in segments with fixed start addresses if ( log ) fprintf(stderr, "Fixed address segments:\n"); for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) continue; + haveFixedSegments = true; if ( segmentsArePageAligned ) { if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { address = _options.customSegmentAddress(sect->segmentName()); @@ -850,8 +920,38 @@ uint64_t InternalState::assignFileOffsets() floatingAddressStart = address; } } - - // second pass, assign section address to sections in segments that are contiguous with previous segment + + // second pass, assign section addresses to sections in segments that are ordered after a segment with a fixed address + if ( haveFixedSegments && !_options.segmentOrder().empty() ) { + if ( log ) fprintf(stderr, "After Fixed address segments:\n"); + lastSegName = ""; + ld::Internal::FinalSection* lastSect = NULL; + for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { + ld::Internal::FinalSection* sect = *it; + if ( (sect->address == ULLONG_MAX) && _options.segmentOrderAfterFixedAddressSegment(sect->segmentName()) ) { + address = lastSect->address + lastSect->size; + if ( (strcmp(lastSegName, sect->segmentName()) != 0) && segmentsArePageAligned ) { + // round up size of last segment + address = pageAlign(address, _options.segPageSize(lastSegName)); + } + // adjust section address based on alignment + uint64_t unalignedAddress = address; + uint64_t alignment = (1 << sect->alignment); + address = ( (unalignedAddress+alignment-1) & (-alignment) ); + sect->alignmentPaddingBytes = (address - unalignedAddress); + sect->address = address; + if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", + sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); + // update running totals + if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) + address += sect->size; + } + lastSegName = sect->segmentName(); + lastSect = sect; + } + } + + // last pass, assign addresses to remaining sections address = floatingAddressStart; lastSegName = ""; ld::Internal::FinalSection* overlappingFixedSection = NULL; @@ -859,7 +959,7 @@ uint64_t InternalState::assignFileOffsets() if ( log ) fprintf(stderr, "Regular layout segments:\n"); for (std::vector::iterator it = sections.begin(); it != sections.end(); ++it) { ld::Internal::FinalSection* sect = *it; - if ( _options.hasCustomSegmentAddress(sect->segmentName()) ) + if ( sect->address != ULLONG_MAX ) continue; if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { sect->alignmentPaddingBytes = 0; @@ -876,6 +976,7 @@ uint64_t InternalState::assignFileOffsets() lastSegName = sect->segmentName(); } } + // adjust section address based on alignment uint64_t unalignedAddress = address; uint64_t alignment = (1 << sect->alignment); @@ -1116,7 +1217,8 @@ int main(int argc, const char* argv[]) ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass ld::passes::dtrace::doPass(options, state); ld::passes::compact_unwind::doPass(options, state); // must be after order pass - + ld::passes::bitcode_bundle::doPass(options, state); // must be after dylib + // sort final sections state.sortSections(); diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp index 889cf2c..c8f272e 100644 --- a/src/ld/ld.hpp +++ b/src/ld/ld.hpp @@ -31,13 +31,19 @@ #include #include +#include +#include #include +#include #include #include "configure.h" namespace ld { +// Forward declaration for bitcode support +class Bitcode; + // // ld::File // @@ -149,8 +155,11 @@ public: virtual uint8_t swiftVersion() const { return 0; } virtual uint32_t cpuSubType() const { return 0; } virtual uint32_t subFileCount() const { return 1; } + virtual uint32_t minOSVersion() const { return 0; } + virtual uint32_t platformLoadCommand() const { return 0; } bool fileExists() const { return _modTime != 0; } Type type() const { return _type; } + virtual Bitcode* getBitcode() const { return NULL; } private: const char* _path; time_t _modTime; @@ -169,7 +178,9 @@ enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100, iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000, iOS_6_0=0x00060000, iOS_7_0=0x00070000, iOS_8_0=0x00080000, iOS_9_0=0x00090000, iOS_Future=0x10000000}; - +enum WatchOSVersionMin { wOSVersionUnset=0, wOS_1_0=0x00010000, wOS_2_0=0x00020000 }; + + namespace relocatable { // // ld::relocatable::File @@ -191,6 +202,7 @@ namespace relocatable { { public: enum DebugInfoKind { kDebugInfoNone=0, kDebugInfoStabs=1, kDebugInfoDwarf=2, kDebugInfoStabsUUID=3 }; + enum SourceKind { kSourceUnknown=0, kSourceObj, kSourceLTO, kSourceArchive, kSourceCompilerArchive, kSourceSingle }; struct Stab { const class Atom* atom; uint8_t type; @@ -211,6 +223,7 @@ namespace relocatable { virtual bool canScatterAtoms() const = 0; virtual bool hasLongBranchStubs() { return false; } virtual LinkerOptionsList* linkerOptions() const = 0; + virtual SourceKind sourceKind() const { return kSourceUnknown; } }; } // namespace relocatable @@ -269,13 +282,15 @@ namespace dylib { virtual bool hasWeakDefinition(const char* name) const = 0; virtual bool hasPublicInstallName() const = 0; virtual bool allSymbolsAreWeakImported() const = 0; - virtual const void* codeSignatureDR() const = 0; virtual bool installPathVersionSpecific() const { return false; } virtual bool appExtensionSafe() const = 0; - virtual MacVersionMin macMinVersion() const { return macVersionUnset; } - virtual IOSVersionMin iOSMinVersion() const { return iOSVersionUnset; } protected: + struct ReExportChain { ReExportChain* prev; const File* file; }; + virtual std::pair hasWeakDefinitionImpl(const char* name) const = 0; + virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const = 0; + virtual void assertNoReExportCycles(ReExportChain*) const = 0; + const char* _dylibInstallPath; uint32_t _dylibTimeStamp; uint32_t _dylibCurrentVersion; @@ -322,7 +337,7 @@ public: typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols, typeTLVDefs, typeTLVZeroFill, typeTLVInitialValues, typeTLVInitializerPointers, typeTLVPointers, - typeFirstSection, typeLastSection, typeDebug }; + typeFirstSection, typeLastSection, typeDebug, typeSectCreate }; Section(const char* sgName, const char* sctName, @@ -644,7 +659,7 @@ public: typeSectionEnd, typeBranchIsland, typeLazyPointer, typeStub, typeNonLazyPointer, typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers, typeLTOtemporary, typeResolver, - typeTLV, typeTLVZeroFill, typeTLVInitialValue, typeTLVInitializerPointers }; + typeTLV, typeTLVZeroFill, typeTLVInitialValue, typeTLVInitializerPointers, typeTLVPointer }; enum SymbolTableInclusion { symbolTableNotIn, symbolTableNotInFinalLinkedImages, symbolTableIn, symbolTableInAndNeverStrip, symbolTableInAsAbsolute, @@ -679,7 +694,8 @@ public: _contentType(ct), _symbolTableInclusion(i), _scope(s), _mode(modeSectionOffset), _overridesADylibsWeakDef(false), _coalescedAway(false), - _live(false), _machoSection(0), _weakImportState(weakImportUnset) + _live(false), _dontDeadStripIfRefLive(false), + _machoSection(0), _weakImportState(weakImportUnset) { #ifndef NDEBUG switch ( _combine ) { @@ -703,6 +719,7 @@ public: ContentType contentType() const { return _contentType; } SymbolTableInclusion symbolTableInclusion() const{ return _symbolTableInclusion; } bool dontDeadStrip() const { return _dontDeadStrip; } + bool dontDeadStripIfReferencesLive() const { return _dontDeadStripIfRefLive; } bool isThumb() const { return _thumb; } bool isAlias() const { return _alias; } Alignment alignment() const { return Alignment(_alignmentPowerOf2, _alignmentModulus); } @@ -722,6 +739,7 @@ public: void setCoalescedAway() { _coalescedAway = true; } void setWeakImportState(bool w) { assert(_definition == definitionProxy); _weakImportState = ( w ? weakImportTrue : weakImportFalse); } void setAutoHide() { _autoHide = true; } + void setDontDeadStripIfReferencesLive() { _dontDeadStripIfRefLive = true; } void setLive() { _live = true; } void setLive(bool value) { _live = value; } void setMachoSection(unsigned x) { assert(x != 0); assert(x < 256); _machoSection = x; } @@ -794,6 +812,7 @@ protected: bool _overridesADylibsWeakDef : 1; bool _coalescedAway : 1; bool _live : 1; + bool _dontDeadStripIfRefLive : 1; unsigned _machoSection : 8; WeakImportState _weakImportState : 2; }; @@ -824,6 +843,7 @@ struct CStringEquals typedef std::unordered_set CStringSet; + class Internal { public: @@ -848,6 +868,8 @@ public: bool hasExternalRelocs; }; + typedef std::map AtomToSection; + virtual uint64_t assignFileOffsets() = 0; virtual void setSectionSizesAndAlignments() = 0; virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0; @@ -858,19 +880,23 @@ public: lazyBindingHelper(NULL), compressedFastBinderProxy(NULL), objcObjectConstraint(ld::File::objcConstraintNone), objcDylibConstraint(ld::File::objcConstraintNone), - swiftVersion(0), cpuSubType(0), + swiftVersion(0), cpuSubType(0), minOSVersion(0), + objectFileFoundWithNoVersion(false), allObjectFilesScatterable(true), someObjectFileHasDwarf(false), usingHugeSections(false), hasThreadLocalVariableDefinitions(false), hasWeakExternalSymbols(false), - someObjectHasOptimizationHints(false) { } - + someObjectHasOptimizationHints(false), + dropAllBitcode(false), embedMarkerOnly(false) { } + std::vector sections; std::vector dylibs; std::vector stabs; + AtomToSection atomToSection; CStringSet linkerOptionLibraries; CStringSet linkerOptionFrameworks; std::vector indirectBindingTable; + std::vector filesWithBitcode; const ld::dylib::File* bundleLoader; const Atom* entryPoint; const Atom* classicBindingHelper; @@ -880,12 +906,18 @@ public: ld::File::ObjcConstraint objcDylibConstraint; uint8_t swiftVersion; uint32_t cpuSubType; + uint32_t minOSVersion; + uint32_t derivedPlatformLoadCommand; + bool objectFileFoundWithNoVersion; bool allObjectFilesScatterable; bool someObjectFileHasDwarf; bool usingHugeSections; bool hasThreadLocalVariableDefinitions; bool hasWeakExternalSymbols; bool someObjectHasOptimizationHints; + bool dropAllBitcode; + bool embedMarkerOnly; + std::string ltoBitcodePath; }; @@ -894,10 +926,6 @@ public: - - - - } // namespace ld #endif // __LD_HPP__ diff --git a/src/ld/lto_file.hpp b/src/ld/lto_file.hpp deleted file mode 100644 index 24d3f58..0000000 --- a/src/ld/lto_file.hpp +++ /dev/null @@ -1,642 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef __LTO_READER_H__ -#define __LTO_READER_H__ - -#include -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "Architectures.hpp" -#include "ld.hpp" - -#include "llvm-c/lto.h" - - -namespace lto { - - -// -// ld64 only tracks non-internal symbols from an llvm bitcode file. -// We model this by having an InternalAtom which represent all internal functions and data. -// All non-interal symbols from a bitcode file are represented by an Atom -// and each Atom has a reference to the InternalAtom. The InternalAtom -// also has references to each symbol external to the bitcode file. -// -class InternalAtom : public ld::Atom -{ -public: - InternalAtom(class File& f); - // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } - virtual const char* name() const { return "import-atom"; } - virtual uint64_t size() const { return 0; } - virtual uint64_t objectAddress() const { return 0; } - virtual void copyRawContent(uint8_t buffer[]) const { } - virtual void setScope(Scope) { } - virtual ld::Fixup::iterator fixupsBegin() { return &_undefs[0]; } - virtual ld::Fixup::iterator fixupsEnd() { return &_undefs[_undefs.size()]; } - - // for adding references to symbols outside bitcode file - void addReference(const char* name) - { _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1, - ld::Fixup::fixupNone, false, name)); } -private: - - ld::File& _file; - std::vector _undefs; -}; - - -// -// LLVM bitcode file -// -class File : public ld::relocatable::File -{ -public: - File(const char* path, time_t mTime, const uint8_t* content, - uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); - virtual ~File(); - - // overrides of ld::File - virtual bool forEachAtom(ld::File::AtomHandler&); - virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) - { return false; } - - // overrides of ld::relocatable::File - virtual bool objcReplacementClasses() { return false; } - virtual DebugInfoKind debugInfo() { return ld::relocatable::File::kDebugInfoNone; } - virtual std::vector* stabs() { return NULL; } - virtual bool canScatterAtoms() { return true; } - - lto_module_t module() { return _module; } - class InternalAtom& internalAtom() { return _internalAtom; } -private: - friend class Atom; - friend class InternalAtom; - - cpu_type_t _architecture; - class InternalAtom _internalAtom; - class Atom* _atomArray; - uint32_t _atomArrayCount; - lto_module_t _module; - ld::Section _section; -}; - -// -// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially, -// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After -// optimization is performed, real Atoms are created for these symobls. However these real Atoms -// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate -// methods to real atom. -// -class Atom : public ld::Atom -{ -public: - Atom(File& f, const char* name, ld::Atom::Scope s, - ld::Atom::Definition d, ld::Atom::Alignment a); - - // overrides of ld::Atom - virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } - virtual const char* name() const { return _name; } - virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } - virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } - virtual void copyRawContent(uint8_t buffer[]) const - { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); } - - ld::Atom* compiledAtom() { return _compiledAtom; } - void setCompiledAtom(ld::Atom& atom) - { _compiledAtom = &atom; } -private: - - File& _file; - const char* _name; - ld::Atom* _compiledAtom; -}; - - - - - - - -class Parser -{ -public: - static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); - static const char* fileKind(const uint8_t* fileContent); - static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, uint32_t ordinal, cpu_type_t architecture); - static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } - static bool optimize(const std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, - const std::set&, - std::vector& newDeadAtoms, - uint32_t nextInputOrdinal, - ld::OutFile* writer, ld::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - bool verbose, bool saveTemps, - const char* outputFilePath, - bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, - bool allowTextRelocs, cpu_type_t arch); - - static const char* ltoVersion() { return ::lto_get_version(); } - -private: - static const char* tripletPrefixForArch(cpu_type_t arch); - static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch); - - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; - - class AtomSyncer : public ld::File::AtomHandler { - public: - AtomSyncer(std::vector& a, std::vector&na, - CStringToAtom la, CStringToAtom dla) : - additionalUndefines(a), newAtoms(na), llvmAtoms(la), deadllvmAtoms(dla) { } - virtual void doAtom(class ld::Atom&); - - std::vector& additionalUndefines; - std::vector& newAtoms; - CStringToAtom llvmAtoms; - CStringToAtom deadllvmAtoms; - }; - - static std::vector _s_files; -}; - -std::vector Parser::_s_files; - - -const char* Parser::tripletPrefixForArch(cpu_type_t arch) -{ - switch (arch) { - case CPU_TYPE_POWERPC: - return "powerpc-"; - case CPU_TYPE_POWERPC64: - return "powerpc64-"; - case CPU_TYPE_I386: - return "i386-"; - case CPU_TYPE_X86_64: - return "x86_64-"; - case CPU_TYPE_ARM: - return "arm"; - } - return ""; -} - -bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture) -{ - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); -} - -const char* Parser::fileKind(const uint8_t* p) -{ - if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { - uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); - switch (arch) { - case CPU_TYPE_POWERPC: - return "ppc"; - case CPU_TYPE_I386: - return "i386"; - case CPU_TYPE_X86_64: - return "x86_64"; - case CPU_TYPE_ARM: - return "arm"; - } - return "unknown bitcode architecture"; - } - return NULL; -} - -File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - uint32_t ordinal, cpu_type_t architecture) -{ - File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); - _s_files.push_back(f); - return f; -} - - -ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch) -{ - switch ( arch ) { - case CPU_TYPE_POWERPC: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_POWERPC64: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_I386: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_X86_64: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - case CPU_TYPE_ARM: - if ( mach_o::relocatable::Parser::validFile(p) ) - return mach_o::relocatable::Parser::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal); - break; - } - throw "LLVM LTO, file is not of required architecture"; -} - - - -File::File(const char* path, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ordinal, cpu_type_t arch) - : ld::relocatable::File(path,mTime,ordinal), _architecture(arch), _internalAtom(*this), - _atomArray(NULL), _atomArrayCount(0), _module(NULL), - _section("__TEXT_", "__tmp_lto", ld::Section::typeUnclassified) -{ - // create llvm module - _module = ::lto_module_create_from_memory(content, contentLength); - if ( _module == NULL ) - throwf("could not parse object file %s: %s", path, lto_get_error_message()); - - // create atom for each global symbol in module - uint32_t count = ::lto_module_get_num_symbols(_module); - _atomArray = (Atom*)malloc(sizeof(Atom)*count); - for (uint32_t i=0; i < count; ++i) { - const char* name = ::lto_module_get_symbol_name(_module, i); - lto_symbol_attributes attr = lto_module_get_symbol_attribute(_module, i); - - // LTO doesn't like dtrace symbols - // ignore dtrace static probes for now - // later when codegen is done and a mach-o file is produces the probes will be processed - if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) ) - continue; - - ld::Atom::Definition def; - switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) { - case LTO_SYMBOL_DEFINITION_REGULAR: - def = ld::Atom::definitionRegular; - break; - case LTO_SYMBOL_DEFINITION_TENTATIVE: - def = ld::Atom::definitionTentative; - break; - case LTO_SYMBOL_DEFINITION_WEAK: - def = ld::Atom::definitionRegular; - break; - case LTO_SYMBOL_DEFINITION_UNDEFINED: - case LTO_SYMBOL_DEFINITION_WEAKUNDEF: - def = ld::Atom::definitionProxy; - break; - default: - throwf("unknown definition kind for symbol %s in bitcode file %s", name, path); - } - - // make LLVM atoms for definitions and a reference for undefines - if ( def != ld::Atom::definitionProxy ) { - ld::Atom::Scope scope; - switch ( attr & LTO_SYMBOL_SCOPE_MASK) { - case LTO_SYMBOL_SCOPE_INTERNAL: - scope = ld::Atom::scopeTranslationUnit; - break; - case LTO_SYMBOL_SCOPE_HIDDEN: - scope = ld::Atom::scopeLinkageUnit; - break; - case LTO_SYMBOL_SCOPE_DEFAULT: - scope = ld::Atom::scopeGlobal; - break; - default: - throwf("unknown scope for symbol %s in bitcode file %s", name, path); - } - // only make atoms for non-internal symbols - if ( scope == ld::Atom::scopeTranslationUnit ) - continue; - uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); - // make Atom using placement new operator - new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, alignment); - } - else { - // add to list of external references - _internalAtom.addReference(name); - } - } -} - -File::~File() -{ - if ( _module != NULL ) - ::lto_module_dispose(_module); -} - -bool File::forEachAtom(ld::File::AtomHandler& handler) -{ - handler.doAtom(_internalAtom); - for(uint32_t i=0; i < _atomArrayCount; ++i) { - handler.doAtom(_atomArray[i]); - } - return true; -} - -InternalAtom::InternalAtom(File& f) - : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, - ld::Atom::typeLTOtemporary, ld::Atom::symbolTableNotIn, false, false, ld::Atom::Alignment(0)), - _file(f) -{ -} - -Atom::Atom(File& f, const char* name, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Alignment a) - : ld::Atom(f._section, d, ld::Atom::combineNever, s, ld::Atom::typeLTOtemporary, ld::Atom::symbolTableIn, false, false, a), - _file(f), _name(name), _compiledAtom(NULL) -{ -} - - - - -bool Parser::optimize(const std::vector& allAtoms, std::vector& newAtoms, - std::vector& additionalUndefines, - const std::set& deadAtoms, - std::vector& newlyDeadAtoms, - uint32_t nextInputOrdinal, - ld::OutFile* writer, ld::Atom* entryPointAtom, - const std::vector& llvmOptions, - bool allGlobalsAReDeadStripRoots, - bool verbose, bool saveTemps, - const char* outputFilePath, - bool pie, bool mainExecutable, bool staticExecutable, bool relocatable, - bool allowTextRelocs, cpu_type_t arch) -{ - // exit quickly if nothing to do - if ( _s_files.size() == 0 ) - return false; - - // print out LTO version string if -v was used - if ( verbose ) - fprintf(stderr, "%s\n", lto_get_version()); - - // create optimizer and add each Reader - lto_code_gen_t generator = ::lto_codegen_create(); - for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - if ( ::lto_codegen_add_module(generator, (*it)->module()) ) - throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); - } - - // add any -mllvm command line options - for (std::vector::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) { - ::lto_codegen_debug_options(generator, *it); - } - - // The atom graph uses directed edges (references). Collect all references where - // originating atom is not part of any LTO Reader. This allows optimizer to optimize an - // external (i.e. not originated from same .o file) reference if all originating atoms are also - // defined in llvm bitcode file. - CStringSet nonLLVMRefs; - CStringToAtom llvmAtoms; - bool hasNonllvmAtoms = false; - for (std::vector::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) { - ld::Atom* atom = *it; - // only look at references that come from an atom that is not an llvm atom - if ( atom->contentType() != ld::Atom::typeLTOtemporary ) { - // remember if we've seen any atoms not from an llvm reader and not from the writer -// if ( atom->getFile() != writer ) -// hasNonllvmAtoms = true; - for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) { - if ( fit->binding != ld::Fixup::bindingByNameBound ) - continue; - // and reference an llvm atom - if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary ) - nonLLVMRefs.insert(fit->u.target->name()); - } - } - else { - llvmAtoms[atom->name()] = (Atom*)atom; - } - } - // if entry point is in a llvm bitcode file, it must be preserved by LTO - if ( entryPointAtom != NULL ) { - if ( entryPointAtom->contentType() == ld::Atom::typeLTOtemporary ) - nonLLVMRefs.insert(entryPointAtom->name()); - } - - // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions - // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced - // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead - // atom so that the linker can replace it with the mach-o one later. - CStringToAtom deadllvmAtoms; - for (std::set::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) { - ld::Atom* atom = *it; - if ( atom->contentType() == ld::Atom::typeLTOtemporary ) { - const char* name = atom->name(); - ::lto_codegen_add_must_preserve_symbol(generator, name); - deadllvmAtoms[name] = (Atom*)atom; - } - } - - - // tell code generator about symbols that must be preserved - for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) { - const char* name = it->first; - Atom* atom = it->second; - // Include llvm Symbol in export list if it meets one of following two conditions - // 1 - atom scope is global (and not linkage unit). - // 2 - included in nonLLVMRefs set. - // If a symbol is not listed in exportList then LTO is free to optimize it away. - if ( (atom->scope() == ld::Atom::scopeGlobal) ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) - ::lto_codegen_add_must_preserve_symbol(generator, name); - } - - // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) - if ( relocatable && !hasNonllvmAtoms ) { - if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) { - // HACK, no good way to tell linker we are all done, so just quit - exit(0); - } - warning("could not produce merged bitcode file"); - } - - // set code-gen model - lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - if ( mainExecutable ) { - if ( staticExecutable ) { - // darwin x86_64 "static" code model is really dynamic code model - if ( arch == CPU_TYPE_X86_64 ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_STATIC; - } - else { - if ( pie ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - } - } - else { - if ( allowTextRelocs ) - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC; - else - model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; - } - if ( ::lto_codegen_set_pic_model(generator, model) ) - throwf("could not create set codegen model: %s", lto_get_error_message()); - - // if requested, save off merged bitcode file - if ( saveTemps ) { - char tempBitcodePath[MAXPATHLEN]; - strcpy(tempBitcodePath, outputFilePath); - strcat(tempBitcodePath, ".lto.bc"); - ::lto_codegen_write_merged_modules(generator, tempBitcodePath); - } - -#if LTO_API_VERSION >= 3 - // find assembler next to linker - char path[PATH_MAX]; - uint32_t bufSize = PATH_MAX; - if ( _NSGetExecutablePath(path, &bufSize) != -1 ) { - char* lastSlash = strrchr(path, '/'); - if ( lastSlash != NULL ) { - strcpy(lastSlash+1, "as"); - struct stat statInfo; - if ( stat(path, &statInfo) == 0 ) - ::lto_codegen_set_assembler_path(generator, path); - } - } -#endif - // run code generator - size_t machOFileLen; - const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); - if ( machOFile == NULL ) - throwf("could not do LTO codegen: %s", ::lto_get_error_message()); - - // if requested, save off temp mach-o file - if ( saveTemps ) { - char tempMachoPath[MAXPATHLEN]; - strcpy(tempMachoPath, outputFilePath); - strcat(tempMachoPath, ".lto.o"); - int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if ( fd != -1) { - ::write(fd, machOFile, machOFileLen); - ::close(fd); - } - // save off merged bitcode file - char tempOptBitcodePath[MAXPATHLEN]; - strcpy(tempOptBitcodePath, outputFilePath); - strcat(tempOptBitcodePath, ".lto.opt.bc"); - ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); - } - - // parse generated mach-o file into a MachOReader - ld::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, arch); - - // sync generated mach-o atoms with existing atoms ld knows about - AtomSyncer syncer(additionalUndefines,newAtoms,llvmAtoms,deadllvmAtoms); - machoFile->forEachAtom(syncer); - - // Remove InternalAtoms from ld - for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - newlyDeadAtoms.push_back(&((*it)->internalAtom())); - } - // Remove Atoms from ld if code generator optimized them away - for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) { - // check if setRealAtom() called on this Atom - if ( li->second->compiledAtom() == NULL ) - newlyDeadAtoms.push_back(li->second); - } - - return true; -} - - -void Parser::AtomSyncer::doAtom(ld::Atom& machoAtom) -{ - // update proxy atoms to point to real atoms and find new atoms - const char* name = machoAtom.name(); - if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { - CStringToAtom::iterator pos = llvmAtoms.find(name); - if ( pos != llvmAtoms.end() ) { - // turn Atom into a proxy for this mach-o atom - pos->second->setCompiledAtom(machoAtom); - } - else { - // an atom of this name was not in the allAtoms list the linker gave us - if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) { - // this corresponding to an atom that the linker coalesced away. - // Don't pass it back as a new atom - } - else - { - // this is something new that lto conjured up, tell ld its new - newAtoms.push_back(&machoAtom); - } - } - } - else { - // ld only knew about non-satic atoms, so this one must be new - newAtoms.push_back(&machoAtom); - } - - // adjust fixups to go through proxy atoms - for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) { - switch ( fit->binding ) { - case ld::Fixup::bindingNone: - break; - case ld::Fixup::bindingByNameUnbound: - // don't know if this target has been seen by linker before or if it is new - // be conservitive and tell linker it is new - additionalUndefines.push_back(fit->u.name); - break; - case ld::Fixup::bindingByNameBound: - break; - case ld::Fixup::bindingDirectlyBound: - // If mach-o atom is referencing another mach-o atom then - // reference is not going through Atom proxy. Fix it here to ensure that all - // llvm symbol references always go through Atom proxy. - break; - case ld::Fixup::bindingByContentBound: - break; - } - } - -} - - - -}; // namespace lto - - -#endif - diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index fbba237..26d70be 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -118,6 +119,9 @@ public: _debugInfoModTime = modTime; _cpuSubType = subtype;} + static bool sSupportsLocalContext; + static bool sHasTriedLocalContext; + bool mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule); private: friend class Atom; friend class InternalAtom; @@ -128,6 +132,9 @@ private: class Atom* _atomArray; uint32_t _atomArrayCount; lto_module_t _module; + const char* _path; + const uint8_t* _content; + uint32_t _contentLength; const char* _debugInfoPath; time_t _debugInfoModTime; ld::Section _section; @@ -303,8 +310,11 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.neverConvertDwarf = false; objOpts.verboseOptimizationHints = options.verboseOptimizationHints; objOpts.armUsesZeroCostExceptions = options.armUsesZeroCostExceptions; - + objOpts.simulator = options.simulator; + objOpts.ignoreMismatchPlatform = options.ignoreMismatchPlatform; + objOpts.platform = options.platform; objOpts.subType = 0; + objOpts.srcKind = ld::relocatable::File::kSourceLTO; // mach-o parsing is done in-memory, but need path for debug notes const char* path = "/tmp/lto.o"; @@ -326,7 +336,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) : ld::relocatable::File(pth,mTime,ordinal), _architecture(arch), _internalAtom(*this), - _atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth), + _atomArray(NULL), _atomArrayCount(0), _module(NULL), _path(pth), + _content(content), _contentLength(contentLength), _debugInfoPath(pth), _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), _fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom), _debugInfo(ld::relocatable::File::kDebugInfoNone), _cpuSubType(0) @@ -334,9 +345,19 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8 const bool log = false; // create llvm module +#if LTO_API_VERSION >= 11 + if ( sSupportsLocalContext || !sHasTriedLocalContext ) { + _module = ::lto_module_create_in_local_context(content, contentLength, pth); + } + if ( !sHasTriedLocalContext ) { + sHasTriedLocalContext = true; + sSupportsLocalContext = (_module != NULL); + } + if ( (_module == NULL) && !sSupportsLocalContext ) +#endif #if LTO_API_VERSION >= 9 _module = ::lto_module_create_from_memory_with_path(content, contentLength, pth); - if ( _module == NULL ) + if ( _module == NULL && !sSupportsLocalContext ) #endif _module = ::lto_module_create_from_memory(content, contentLength); if ( _module == NULL ) @@ -417,6 +438,11 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8 if ( log ) fprintf(stderr, "\t%s (undefined)\n", name); } } + +#if LTO_API_VERSION >= 11 + if ( sSupportsLocalContext ) + this->release(); +#endif } File::~File() @@ -424,6 +450,34 @@ File::~File() this->release(); } +bool File::mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule) { +#if LTO_API_VERSION >= 11 + if ( sSupportsLocalContext ) { + assert(!_module && "Expected module to be disposed"); + _module = ::lto_module_create_in_codegen_context(_content, _contentLength, + _path, generator); + if ( _module == NULL ) + throwf("could not reparse object file %s: '%s', using libLTO version '%s'", + _path, ::lto_get_error_message(), ::lto_get_version()); + } +#endif + assert(_module && "Expected module to stick around"); +#if LTO_API_VERSION >= 13 + if (useSetModule) { + // lto_codegen_set_module will transfer ownership of the module to LTO code generator, + // so we don't need to release the module here. + ::lto_codegen_set_module(generator, _module); + return false; + } +#endif + if ( ::lto_codegen_add_module(generator, _module) ) + return true; + + // linker should release module as soon as possible + this->release(); + return false; +} + void File::release() { if ( _module != NULL ) @@ -486,6 +540,8 @@ void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, co switch ( severity ) { #if LTO_API_VERSION >= 10 case LTO_DS_REMARK: + fprintf(stderr, "ld: LTO remark: %s\n", message); + break; #endif case LTO_DS_NOTE: case LTO_DS_WARNING: @@ -518,7 +574,13 @@ bool Parser::optimize( const std::vector& allAtoms, fprintf(stderr, "%s\n", ::lto_get_version()); // create optimizer and add each Reader - lto_code_gen_t generator = ::lto_codegen_create(); + lto_code_gen_t generator = NULL; +#if LTO_API_VERSION >= 11 + if ( File::sSupportsLocalContext ) + generator = ::lto_codegen_create_in_local_context(); + else +#endif + generator = ::lto_codegen_create(); #if LTO_API_VERSION >= 7 lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL); #endif @@ -526,14 +588,20 @@ bool Parser::optimize( const std::vector& allAtoms, // The order that files are merged must match command line order std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); ld::File::Ordinal lastOrdinal; + + // When flto_codegen_only is on and we have a single .bc file, use lto_codegen_set_module instead of + // lto_codegen_add_module, to make sure the the destination module will be the same as the input .bc file. + bool useSetModule = false; +#if LTO_API_VERSION >= 13 + useSetModule = (_s_files.size() == 1) && options.ltoCodegenOnly && (::lto_api_version() >= 13); +#endif for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { File* f = *it; assert(f->ordinal() > lastOrdinal); - if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); - if ( ::lto_codegen_add_module(generator, f->module()) ) + if ( logBitcodeFiles && !useSetModule) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); + if ( logBitcodeFiles && useSetModule) fprintf(stderr, "lto_codegen_set_module(%s)\n", f->path()); + if ( f->mergeIntoGenerator(generator, useSetModule) ) throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); - // linker should release module as soon as possible - f->release(); lastOrdinal = f->ordinal(); } @@ -653,6 +721,9 @@ bool Parser::optimize( const std::vector& allAtoms, // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o) if ( options.relocatable && !hasNonllvmAtoms ) { +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, false); +#endif if ( ! ::lto_codegen_write_merged_modules(generator, options.outputFilePath) ) { // HACK, no good way to tell linker we are all done, so just quit exit(0); @@ -691,6 +762,9 @@ bool Parser::optimize( const std::vector& allAtoms, char tempBitcodePath[MAXPATHLEN]; strcpy(tempBitcodePath, options.outputFilePath); strcat(tempBitcodePath, ".lto.bc"); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, true); +#endif ::lto_codegen_write_merged_modules(generator, tempBitcodePath); } @@ -708,11 +782,62 @@ bool Parser::optimize( const std::vector& allAtoms, } } #endif - // run code generator - size_t machOFileLen; - const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); - if ( machOFile == NULL ) - throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + + // When lto API version is greater than or equal to 12, we use lto_codegen_optimize and lto_codegen_compile_optimized + // instead of lto_codegen_compile, and we save the merged bitcode file in between. + bool useSplitAPI = false; +#if LTO_API_VERSION >= 12 + if ( ::lto_api_version() >= 12) + useSplitAPI = true; +#endif + + size_t machOFileLen = 0; + const uint8_t* machOFile = NULL; + if ( useSplitAPI) { +#if LTO_API_VERSION >= 12 +#if LTO_API_VERSION >= 14 + if ( ::lto_api_version() >= 14 && options.ltoCodegenOnly) + lto_codegen_set_should_internalize(generator, false); +#endif + // run optimizer + if ( !options.ltoCodegenOnly && ::lto_codegen_optimize(generator) ) + throwf("could not do LTO optimization: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + + if ( options.saveTemps || options.bitcodeBundle ) { + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, options.outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, true); +#endif + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + if ( options.bitcodeBundle ) + state.ltoBitcodePath = tempOptBitcodePath; + } + + // run code generator + machOFile = (uint8_t*)::lto_codegen_compile_optimized(generator, &machOFileLen); +#endif + if ( machOFile == NULL ) + throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + } + else { + // run optimizer and code generator + machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); + if ( machOFile == NULL ) + throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); + if ( options.saveTemps ) { + // save off merged bitcode file + char tempOptBitcodePath[MAXPATHLEN]; + strcpy(tempOptBitcodePath, options.outputFilePath); + strcat(tempOptBitcodePath, ".lto.opt.bc"); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(generator, true); +#endif + ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); + } + } // if requested, save off temp mach-o file if ( options.saveTemps ) { @@ -724,11 +849,6 @@ bool Parser::optimize( const std::vector& allAtoms, ::write(fd, machOFile, machOFileLen); ::close(fd); } - // save off merged bitcode file - char tempOptBitcodePath[MAXPATHLEN]; - strcpy(tempOptBitcodePath, options.outputFilePath); - strcat(tempOptBitcodePath, ".lto.opt.bc"); - ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath); } // if needed, save temp mach-o file to specific location @@ -860,7 +980,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) } else { // Don't unbind follow-on reference into by-name reference - if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) { + if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) && (fit->u.target->scope() != ld::Atom::scopeTranslationUnit) ) { // target was coalesed away and replace by mach-o atom from a non llvm .o file fit->binding = ld::Fixup::bindingByNameUnbound; fit->u.name = targetName; @@ -888,6 +1008,8 @@ public: ~Mutex() { pthread_mutex_unlock(<o_lock); } }; pthread_mutex_t Mutex::lto_lock = PTHREAD_MUTEX_INITIALIZER; +bool File::sSupportsLocalContext = false; +bool File::sHasTriedLocalContext = false; // // Used by archive reader to see if member is an llvm bitcode file @@ -899,6 +1021,18 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar } +static ld::relocatable::File *parseImpl( + const uint8_t *fileContent, uint64_t fileLength, const char *path, + time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, + cpu_subtype_t subarch, bool logAllFiles, + bool verboseOptimizationHints) +{ + if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints); + else + return NULL; +} + // // main function used by linker to instantiate ld::Files // @@ -907,11 +1041,12 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints) { + // Note: Once lto_module_create_in_local_context() and friends are thread safe + // this lock can be removed. Mutex lock; - if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints); - else - return NULL; + return parseImpl(fileContent, fileLength, path, modTime, ordinal, + architecture, subarch, logAllFiles, + verboseOptimizationHints); } // diff --git a/src/ld/parsers/lto_file.h b/src/ld/parsers/lto_file.h index dcd6e36..6568232 100644 --- a/src/ld/parsers/lto_file.h +++ b/src/ld/parsers/lto_file.h @@ -48,6 +48,7 @@ struct OptimizeOptions { bool preserveAllGlobals; bool verbose; bool saveTemps; + bool ltoCodegenOnly; bool pie; bool mainExecutable; bool staticExecutable; @@ -58,8 +59,12 @@ struct OptimizeOptions { bool keepDwarfUnwind; bool verboseOptimizationHints; bool armUsesZeroCostExceptions; + bool simulator; + bool ignoreMismatchPlatform; + bool bitcodeBundle; cpu_type_t arch; const char* mcpu; + Options::Platform platform; const std::vector* llvmOptions; const std::vector* initialUndefines; }; diff --git a/src/ld/parsers/macho_dylib_file.cpp b/src/ld/parsers/macho_dylib_file.cpp index a3c1456..0ba962c 100644 --- a/src/ld/parsers/macho_dylib_file.cpp +++ b/src/ld/parsers/macho_dylib_file.cpp @@ -1,4 +1,3 @@ - /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2005-2011 Apple Inc. All rights reserved. @@ -33,11 +32,13 @@ #include #include +#include #include #include #include #include "Architectures.hpp" +#include "Bitcode.hpp" #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" #include "macho_dylib_file.h" @@ -143,8 +144,10 @@ 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 allowSimToMacOSX, bool addVers, - bool logAllFiles, const char* installPath, bool indirectDylib); + Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, + bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* installPath, + bool indirectDylib, bool ignoreMismatchPlatform); virtual ~File() {} // overrides of ld::File @@ -152,7 +155,9 @@ public: virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; virtual ld::File::ObjcConstraint objCConstraint() const { return _objcContraint; } virtual uint8_t swiftVersion() const { return _swiftVersion; } - + virtual uint32_t minOSVersion() const { return _minVersionInDylib; } + virtual uint32_t platformLoadCommand() const { return _platformInDylib; } + // overrides of ld::dylib::File virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); virtual bool providedExportAtom() const { return _providedAtom; } @@ -163,18 +168,12 @@ public: virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } virtual bool hasWeakDefinition(const char* name) const; virtual bool allSymbolsAreWeakImported() const; - virtual const void* codeSignatureDR() const { return _codeSignatureDR; } virtual bool installPathVersionSpecific() const { return _installPathOverride; } virtual bool appExtensionSafe() const { return _appExtensionSafe; }; - virtual ld::MacVersionMin macMinVersion() const { return _macMinVersionInDylib; } - virtual ld::IOSVersionMin iOSMinVersion() const { return _iOSMinVersionInDylib; } - + virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } protected: - - struct ReExportChain { ReExportChain* prev; File* file; }; - - void assertNoReExportCycles(ReExportChain*); + virtual void assertNoReExportCycles(ReExportChain*) const; private: typedef typename A::P P; @@ -192,13 +191,14 @@ private: return size_t(__h); }; }; - struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; pint_t address; }; + struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; uint64_t address; }; typedef std::unordered_map NameToAtomMap; typedef std::unordered_set NameSet; struct Dependent { const char* path; File* dylib; bool reExport; }; - bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const; + virtual std::pair hasWeakDefinitionImpl(const char* name) const; + virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const; bool isPublicLocation(const char* pth); bool wrongOS() { return _wrongOS; } void addSymbol(const char* name, bool weak, bool tlv, pint_t address); @@ -211,9 +211,9 @@ private: static uint32_t parseVersionNumber32(const char* versionString); static const char* objCInfoSegmentName(); static const char* objCInfoSectionName(); - - const ld::MacVersionMin _macVersionMin; - const ld::IOSVersionMin _iOSVersionMin; + + const Options::Platform _platform; + const uint32_t _linkMinOSVersion; const bool _allowSimToMacOSXLinking; const bool _addVersionLoadCommand; bool _linkingFlat; @@ -228,7 +228,6 @@ private: NameSet _ignoreExports; const char* _parentUmbrella; ImportAtom* _importAtom; - const void* _codeSignatureDR; bool _noRexports; bool _hasWeakExports; bool _deadStrippable; @@ -239,8 +238,9 @@ private: bool _installPathOverride; bool _indirectDylibsProcessed; bool _appExtensionSafe; - ld::MacVersionMin _macMinVersionInDylib; - ld::IOSVersionMin _iOSMinVersionInDylib; + uint32_t _minVersionInDylib; + uint32_t _platformInDylib; + std::unique_ptr _bitcode; static bool _s_logHashtable; }; @@ -259,20 +259,20 @@ template const char* File::objCInfoSectionName() { return "__ima template File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, - ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool allowSimToMacOSX, bool addVers, - bool logAllFiles, const char* targetInstallPath, bool indirectDylib) + Options::Platform platform, uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* targetInstallPath, bool indirectDylib, bool ignoreMismatchPlatform) : ld::dylib::File(strdup(pth), mTime, ord), - _macVersionMin(macMin), _iOSVersionMin(iOSMin), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), + _platform(platform), _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), _objcContraint(ld::File::objcConstraintNone), _swiftVersion(0), _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), - _parentUmbrella(NULL), _importAtom(NULL), _codeSignatureDR(NULL), + _parentUmbrella(NULL), _importAtom(NULL), _noRexports(false), _hasWeakExports(false), _deadStrippable(false), _hasPublicInstallName(false), _providedAtom(false), _explictReExportFound(false), _wrongOS(false), _installPathOverride(false), _indirectDylibsProcessed(false), _appExtensionSafe(false), - _macMinVersionInDylib(ld::macVersionUnset), _iOSMinVersionInDylib(ld::iOSVersionUnset) + _minVersionInDylib(0), _platformInDylib(Options::kPlatformUnknown) { const macho_header

* header = (const macho_header

*)fileContent; const uint32_t cmd_count = header->ncmds(); @@ -302,11 +302,11 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, // pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format const macho_dysymtab_command

* dynamicInfo = NULL; const macho_dyld_info_command

* dyldInfo = NULL; - const macho_linkedit_data_command

* codeSignature = NULL; const macho_nlist

* symbolTable = NULL; const char* strings = NULL; bool compressedLinkEdit = false; uint32_t dependentLibCount = 0; + Options::Platform lcPlatform = Options::kPlatformUnknown; const macho_load_command

* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { macho_dylib_command

* dylibID; @@ -348,29 +348,24 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, break; case LC_SUB_CLIENT: _allowableClients.push_back(strdup(((macho_sub_client_command

*)cmd)->client())); + // Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked + _hasPublicInstallName = false; break; case LC_VERSION_MIN_MACOSX: - if ( (_iOSVersionMin != ld::iOSVersionUnset) && !_allowSimToMacOSXLinking ) { - _wrongOS = true; - if ( _addVersionLoadCommand && !indirectDylib ) - throw "building for iOS Simulator, but linking against dylib built for MacOSX"; - } - _macMinVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); - break; case LC_VERSION_MIN_IPHONEOS: - if ( _macVersionMin != ld::macVersionUnset ) { - _wrongOS = true; - if ( _addVersionLoadCommand && !indirectDylib ) - throw "building for MacOSX, but linking against dylib built for iOS Simulator"; - } - _iOSMinVersionInDylib = (ld::IOSVersionMin)((macho_version_min_command

*)cmd)->version(); + case LC_VERSION_MIN_WATCHOS: + #if SUPPORT_APPLE_TV + case LC_VERSION_MIN_TVOS: + #endif + _minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command

*)cmd)->version(); + _platformInDylib = cmd->cmd(); + lcPlatform = Options::platformForLoadCommand(_platformInDylib); break; case LC_CODE_SIGNATURE: - codeSignature = (macho_linkedit_data_command

* )cmd; break; case macho_segment_command

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

*)cmd)->segname(), objCInfoSegmentName()) == 0 ) { + if ( strncmp(((macho_segment_command

*)cmd)->segname(), objCInfoSegmentName(), 6) == 0 ) { const macho_segment_command

* segment = (macho_segment_command

*)cmd; const macho_section

* const sectionsStart = (macho_section

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

)); const macho_section

* const sectionsEnd = §ionsStart[segment->nsects()]; @@ -403,11 +398,88 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, } } } + // Construct bitcode if there is a bitcode bundle section in the dylib + // Record the size of the section because the content is not checked + else if ( strcmp(((macho_segment_command

*)cmd)->segname(), "__LLVM") == 0 ) { + const macho_section

* const sect = (macho_section

*)((char*)cmd + sizeof(macho_segment_command

)); + if ( strncmp(sect->sectname(), "__bundle", 8) == 0 ) + _bitcode = std::unique_ptr(new ld::Bitcode(NULL, sect->size())); + } } cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); if ( cmd > cmdsEnd ) throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth); } + // arm/arm64 objects are default to ios platform if not set. + // rdar://problem/21746314 + if (lcPlatform == Options::kPlatformUnknown && + (std::is_same::value || std::is_same::value)) + lcPlatform = Options::kPlatformiOS; + + // check cross-linking + if ( lcPlatform != platform ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib && !ignoreMismatchPlatform ) { + if ( buildingForSimulator ) { + if ( !_allowSimToMacOSXLinking ) { + switch (platform) { + case Options::kPlatformOSX: + case Options::kPlatformiOS: + if ( lcPlatform == Options::kPlatformUnknown ) + break; + // fall through if the Platform is not Unknown + case Options::kPlatformWatchOS: + // WatchOS errors on cross-linking all the time. + throwf("building for %s simulator, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + // tvOS is a warning temporarily. rdar://problem/21746965 + if (platform == Options::kPlatform_tvOS) + warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path(), + Options::platformName(lcPlatform)); + break; + #endif + case Options::kPlatformUnknown: + // skip if the target platform is unknown + break; + } + } + } + else { + switch (platform) { + case Options::kPlatformOSX: + case Options::kPlatformiOS: + if ( lcPlatform == Options::kPlatformUnknown ) + break; + // fall through if the Platform is not Unknown + case Options::kPlatformWatchOS: + // WatchOS errors on cross-linking all the time. + throwf("building for %s, but linking against dylib built for %s,", + Options::platformName(platform), + Options::platformName(lcPlatform)); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + // tvOS is a warning temporarily. rdar://problem/21746965 + if (platform == Options::kPlatform_tvOS) + warning("URGENT: building for %s, but linking against dylib (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), path(), + Options::platformName(lcPlatform)); + break; + #endif + case Options::kPlatformUnknown: + // skip if the target platform is unknown + break; + } + } + } + } // figure out if we need to examine dependent dylibs // with compressed LINKEDIT format, MH_NO_REEXPORTED_DYLIBS can be trusted @@ -441,7 +513,8 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, } // verify MH_NO_REEXPORTED_DYLIBS bit was correct if ( compressedLinkEdit && !linkingFlatNamespace ) { - assert(reExportDylibCount != 0); + if ( reExportDylibCount == 0 ) + throwf("malformed dylib has MH_NO_REEXPORTED_DYLIBS flag but no LC_REEXPORT_DYLIB load commands: %s", pth); } // pass 3 add re-export info cmd = cmds; @@ -500,26 +573,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, } _importAtom = new ImportAtom(*this, importNames); } - - // if the dylib is code signed, look for its Designated Requirement - if ( codeSignature != NULL ) { - const Security::BlobCore* overallSignature = (Security::BlobCore*)((char*)header + codeSignature->dataoff()); - typedef Security::SuperBlob EmbeddedSignatureBlob; - typedef Security::SuperBlob InternalRequirementsBlob; - const EmbeddedSignatureBlob* signature = EmbeddedSignatureBlob::specific(overallSignature); - if ( signature->validateBlob(codeSignature->datasize()) ) { - const InternalRequirementsBlob* ireq = signature->find(Security::cdRequirementsSlot); - if ( (ireq != NULL) && ireq->validateBlob() ) { - const Security::BlobCore* dr = ireq->find(Security::kSecDesignatedRequirementType); - if ( (dr != NULL) && dr->validateBlob(Security::kSecCodeMagicRequirement) ) { - // make copy because mapped file is about to be unmapped - _codeSignatureDR = ::malloc(dr->length()); - ::memcpy((void*)_codeSignatureDR, dr, dr->length()); - } - } - } - } - + // build hash table if ( dyldInfo != NULL ) buildExportHashTableFromExportInfo(dyldInfo, fileContent); @@ -530,6 +584,7 @@ File::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, munmap((caddr_t)fileContent, fileLength); } + // // Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz // @@ -634,15 +689,7 @@ void File::addSymbol(const char* name, bool weakDef, bool tlv, pint_t address const char* symCond = strchr(symAction, '$'); if ( symCond != NULL ) { char curOSVers[16]; - if ( _macVersionMin != ld::macVersionUnset ) { - sprintf(curOSVers, "$os%d.%d$", (_macVersionMin >> 16), ((_macVersionMin >> 8) & 0xFF)); - } - else if ( _iOSVersionMin != ld::iOSVersionUnset ) { - sprintf(curOSVers, "$os%d.%d$", (_iOSVersionMin >> 16), ((_iOSVersionMin >> 8) & 0xFF)); - } - else { - assert(0 && "targeting neither macosx nor iphoneos"); - } + sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { const char* symName = strchr(&symCond[1], '$'); if ( symName != NULL ) { @@ -705,29 +752,34 @@ bool File::forEachAtom(ld::File::AtomHandler& handler) const return false; } + +template +std::pair File::hasWeakDefinitionImpl(const char* name) const +{ + const auto pos = _atoms.find(name); + if ( pos != _atoms.end() ) + return std::make_pair(true, pos->second.weakDef); + + // look in children that I re-export + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport ) { + auto ret = dep.dylib->hasWeakDefinitionImpl(name); + if ( ret.first ) + return ret; + } + } + return std::make_pair(false, false); +} + + template bool File::hasWeakDefinition(const char* name) const { // if supposed to ignore this export, then pretend I don't have it if ( _ignoreExports.count(name) != 0 ) return false; - - typename NameToAtomMap::const_iterator pos = _atoms.find(name); - if ( pos != _atoms.end() ) { - return pos->second.weakDef; - } - else { - // look in children that I re-export - for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - if ( it->reExport ) { - //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->path(), (*it)->getInstallPath()); - typename NameToAtomMap::iterator cpos = it->dylib->_atoms.find(name); - if ( cpos != it->dylib->_atoms.end() ) - return cpos->second.weakDef; - } - } - } - return false; + + return hasWeakDefinitionImpl(name).second; } @@ -756,24 +808,24 @@ bool File::allSymbolsAreWeakImported() const template -bool File::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const +bool File::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& defAddress) const { if ( _ignoreExports.count(name) != 0 ) return false; // check myself - typename NameToAtomMap::iterator pos = _atoms.find(name); + const auto pos = _atoms.find(name); if ( pos != _atoms.end() ) { - *weakDef = pos->second.weakDef; - *tlv = pos->second.tlv; - *defAddress = pos->second.address; + weakDef = pos->second.weakDef; + tlv = pos->second.tlv; + defAddress = pos->second.address; return true; } // check dylibs I re-export - for (typename std::vector::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) { - if ( it->reExport && !it->dylib->implicitlyLinked() ) { - if ( it->dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport && !dep.dylib->implicitlyLinked() ) { + if ( dep.dylib->containsOrReExports(name, weakDef, tlv, defAddress) ) return true; } } @@ -791,7 +843,7 @@ bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& han AtomAndWeak bucket; - if ( this->containsOrReExports(name, &bucket.weakDef, &bucket.tlv, &bucket.address) ) { + if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, bucket.address) ) { bucket.atom = new ExportAtom(*this, name, bucket.weakDef, bucket.tlv, bucket.address); _atoms[name] = bucket; _providedAtom = true; @@ -903,23 +955,23 @@ void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, b } template -void File::assertNoReExportCycles(ReExportChain* prev) +void File::assertNoReExportCycles(ReExportChain* prev) const { // recursively check my re-exported dylibs ReExportChain chain; chain.prev = prev; chain.file = this; - for (typename std::vector::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) { - if ( it->reExport ) { - ld::File* child = it->dylib; + for (const auto &dep : _dependentDylibs) { + if ( dep.reExport ) { + ld::File* child = dep.dylib; // check child is not already in chain - for (ReExportChain* p = prev; p != NULL; p = p->prev) { + for (ReExportChain* p = prev; p != nullptr; p = p->prev) { if ( p->file == child ) { throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); } } - if ( it->dylib != NULL ) - it->dylib->assertNoReExportCycles(&chain); + if ( dep.dylib != nullptr ) + dep.dylib->assertNoReExportCycles(&chain); } } } @@ -940,13 +992,15 @@ public: ordinal, opts.flatNamespace(), opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), - opts.macosxVersionMin(), - opts.iOSVersionMin(), + opts.platform(), + opts.minOSversion(), opts.allowSimulatorToLinkWithMacOSX(), opts.addVersionLoadCommand(), - opts.logAllFiles(), + opts.targetIOSSimulator(), + opts.logAllFiles(), opts.installPath(), - indirectDylib); + indirectDylib, + opts.outputKind() == Options::kPreload); } }; diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index 49d333e..c023f37 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -42,11 +42,13 @@ #include #include #include +#include #include "dwarf2.h" #include "debugline.h" #include "Architectures.hpp" +#include "Bitcode.hpp" #include "ld.hpp" #include "macho_relocatable_file.h" @@ -73,7 +75,7 @@ public: File(const char* p, time_t mTime, const uint8_t* content, ld::File::Ordinal ord) : ld::relocatable::File(p,mTime,ord), _fileContent(content), _sectionsArray(NULL), _atomsArray(NULL), - _sectionsArrayCount(0), _atomsArrayCount(0), + _sectionsArrayCount(0), _atomsArrayCount(0), _aliasAtomsArrayCount(0), _debugInfoKind(ld::relocatable::File::kDebugInfoNone), _dwarfTranslationUnitPath(NULL), _dwarfDebugInfoSect(NULL), _dwarfDebugAbbrevSect(NULL), @@ -81,14 +83,19 @@ public: _objConstraint(ld::File::objcConstraintNone), _swiftVersion(0), _cpuSubType(0), - _canScatterAtoms(false) {} + _minOSVersion(0), + _platform(0), + _canScatterAtoms(false), + _srcKind(kSourceUnknown) {} virtual ~File(); // overrides of ld::File virtual bool forEachAtom(ld::File::AtomHandler&) const; virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const { return false; } - + virtual uint32_t minOSVersion() const { return _minOSVersion; } + virtual uint32_t platformLoadCommand() const { return _platform; } + // overrides of ld::relocatable::File virtual ObjcConstraint objCConstraint() const { return _objConstraint; } virtual uint32_t cpuSubType() const { return _cpuSubType; } @@ -98,6 +105,9 @@ public: virtual const char* translationUnitSource() const; virtual LinkerOptionsList* linkerOptions() const { return &_linkerOptions; } virtual uint8_t swiftVersion() const { return _swiftVersion; } + virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } + virtual SourceKind sourceKind() const { return _srcKind; } + virtual void setSourceKind(SourceKind src) { _srcKind = src; } const uint8_t* fileContent() { return _fileContent; } private: @@ -128,8 +138,12 @@ private: ld::File::ObjcConstraint _objConstraint; uint8_t _swiftVersion; uint32_t _cpuSubType; + uint32_t _minOSVersion; + uint32_t _platform; bool _canScatterAtoms; std::vector > _linkerOptions; + std::unique_ptr _bitcode; + SourceKind _srcKind; }; @@ -148,6 +162,7 @@ public: virtual ld::Atom::Alignment alignmentForAddress(pint_t addr); virtual ld::Atom::ContentType contentType() { return ld::Atom::typeUnclassified; } virtual bool dontDeadStrip() { return (this->_machOSection->flags() & S_ATTR_NO_DEAD_STRIP); } + virtual bool dontDeadStripIfReferencesLive() { return ( (this->_machOSection != NULL) && (this->_machOSection->flags() & S_ATTR_LIVE_SUPPORT) ); } virtual Atom* findAtomByAddress(pint_t addr) { return this->findContentAtomByAddress(addr, this->_beginAtoms, this->_endAtoms); } virtual bool addFollowOnFixups() const { return ! _file.canScatterAtoms(); } virtual uint32_t appendAtoms(class Parser& parser, uint8_t* buffer, @@ -506,6 +521,30 @@ private: static ld::Fixup::Kind fixupKind(); }; +template +class TLVPointerSection : public FixedSizeSection +{ +public: + TLVPointerSection(Parser& parser, File& f, const macho_section* s) + : FixedSizeSection(parser, f, s) {} +protected: + typedef typename A::P::uint_t pint_t; + typedef typename A::P P; + + virtual ld::Atom::ContentType contentType() { return ld::Atom::typeTLVPointer; } + virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); } + virtual const char* unlabeledAtomName(Parser&, pint_t) { return "tlv_lazy_ptr"; } + virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); } + virtual ld::Atom::Combine combine(Parser&, pint_t); + virtual bool ignoreLabel(const char* label) const { return true; } + virtual unsigned long contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const; + virtual bool canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& ind) const; + +private: + static const char* targetName(const class Atom* atom, const ld::IndirectBindingTable& ind, bool* isStatic); +}; + template class CFStringSection : public FixedSizeSection @@ -752,7 +791,7 @@ public: parser.combineFromSymbol(sym), parser.scopeFromSymbol(sym), parser.resolverFromSymbol(sym) ? ld::Atom::typeResolver : sct.contentType(), parser.inclusionFromSymbol(sym), - parser.dontDeadStripFromSymbol(sym) || sct.dontDeadStrip(), + (parser.dontDeadStripFromSymbol(sym) && !sct.dontDeadStripIfReferencesLive()) || sct.dontDeadStrip(), parser.isThumbFromSymbol(sym), alias, sct.alignmentForAddress(sym.n_value())), _size(sz), _objAddress(sym.n_value()), @@ -764,7 +803,9 @@ 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(*sct.machoSection()); + this->verifyAlignment(*sct.machoSection()); + if ( sct.dontDeadStripIfReferencesLive() ) + this->setDontDeadStripIfReferencesLive(); } private: @@ -936,15 +977,18 @@ public: static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0); static const char* fileKind(const uint8_t* fileContent); + static Options::Platform findPlatform(const macho_header* header); static bool hasObjC2Categories(const uint8_t* fileContent); static bool hasObjC1Categories(const uint8_t* fileContent); + static bool getNonLocalSymbols(const uint8_t* fileContnet, std::vector &syms); static ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) { Parser p(fileContent, fileLength, path, modTime, ordinal, opts.warnUnwindConversionProblems, opts.keepDwarfUnwind, opts.forceDwarfConversion, - opts.neverConvertDwarf, opts.verboseOptimizationHints); + opts.neverConvertDwarf, opts.verboseOptimizationHints, + opts.ignoreMismatchPlatform); return p.parse(opts); } @@ -1114,7 +1158,7 @@ private: sectionTypeUTF16Strings, sectionTypeCFString, sectionTypeObjC2ClassRefs, typeObjC2CategoryList, sectionTypeObjC1Classes, sectionTypeSymboled, sectionTypeObjC1ClassRefs, sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs, - sectionTypeCompactUnwind }; + sectionTypeCompactUnwind, sectionTypeTLVPointers}; template struct MachOSectionAndSectionClass @@ -1141,10 +1185,11 @@ private: Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, bool warnUnwindConversionProblems, bool keepDwarfUnwind, - bool forceDwarfConversion, bool neverConvertDwarf, bool verboseOptimizationHints); + bool forceDwarfConversion, bool neverConvertDwarf, + bool verboseOptimizationHints, bool ignoreMismatchPlatform); ld::relocatable::File* parse(const ParserOptions& opts); - uint8_t loadCommandSizeMask(); - bool parseLoadCommands(); + static uint8_t loadCommandSizeMask(); + bool parseLoadCommands(Options::Platform platform, uint32_t minOSVersion, bool simulator, bool ignoreMismatchPlatform); void makeSections(); void prescanSymbolTable(); void makeSortedSymbolsArray(uint32_t symArray[], const uint32_t sectionArray[]); @@ -1209,6 +1254,7 @@ private: bool _neverConvertDwarf; bool _verboseOptimizationHints; bool _armUsesZeroCostExceptions; + bool _ignoreMismatchPlatform; unsigned int _stubsSectionNum; const macho_section

* _stubsMachOSection; std::vector _dtraceProviderInfo; @@ -1220,7 +1266,7 @@ private: template Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, bool convertDUI, bool keepDwarfUnwind, bool forceDwarfConversion, - bool neverConvertDwarf, bool verboseOptimizationHints) + bool neverConvertDwarf, bool verboseOptimizationHints, bool ignoreMismatchPlatform) : _fileContent(fileContent), _fileLength(fileLength), _path(path), _modTime(modTime), _ordinal(ordinal), _file(NULL), _symbols(NULL), _symbolCount(0), _indirectSymbolCount(0), _strings(NULL), _stringsSize(0), @@ -1236,6 +1282,7 @@ Parser::Parser(const uint8_t* fileContent, uint64_t fileLength, const char* p _keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion), _neverConvertDwarf(neverConvertDwarf), _verboseOptimizationHints(verboseOptimizationHints), + _ignoreMismatchPlatform(ignoreMismatchPlatform), _stubsSectionNum(0), _stubsMachOSection(NULL) { } @@ -1318,7 +1365,7 @@ template <> const char* Parser::fileKind(const uint8_t* fileContent) { const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) + if ( header->magic() != MH_MAGIC_64 ) return NULL; if ( header->cputype() != CPU_TYPE_X86_64 ) return NULL; @@ -1346,7 +1393,7 @@ template <> const char* Parser::fileKind(const uint8_t* fileContent) { const macho_header

* header = (const macho_header

*)fileContent; - if ( header->magic() != MH_MAGIC ) + if ( header->magic() != MH_MAGIC_64 ) return NULL; if ( header->cputype() != CPU_TYPE_ARM64 ) return NULL; @@ -1411,6 +1458,39 @@ bool Parser::hasObjC1Categories(const uint8_t* fileContent) return false; } + +template +bool Parser::getNonLocalSymbols(const uint8_t* fileContent, std::vector &syms) +{ + const macho_header

* header = (const macho_header

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

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == LC_SYMTAB ) { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + uint32_t symbolCount = symtab->nsyms(); + const macho_nlist

* symbols = (const macho_nlist

*)(fileContent + symtab->symoff()); + const char* strings = (char*)fileContent + symtab->stroff(); + for (uint32_t i = 0; i < symbolCount; ++i) { + // ignore stabs and count only ext symbols + if ( (symbols[i].n_type() & N_STAB) == 0 && + (symbols[i].n_type() & N_EXT) != 0 ) { + const char* symName = &strings[symbols[i].n_strx()]; + syms.push_back(symName); + } + } + return true; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + return false; +} + + template int Parser::pointerSorter(const void* l, const void* r) { @@ -1634,6 +1714,9 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // create file object _file = new File(_path, _modTime, _fileContent, _ordinal); + // set input source + _file->setSourceKind(opts.srcKind); + // respond to -t option if ( opts.logAllFiles ) printf("%s\n", _path); @@ -1641,7 +1724,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) _armUsesZeroCostExceptions = opts.armUsesZeroCostExceptions; // parse start of mach-o file - if ( ! parseLoadCommands() ) + if ( ! parseLoadCommands(opts.platform, opts.minOSVersion, opts.simulator, opts.ignoreMismatchPlatform) ) return _file; // make array of @@ -1797,7 +1880,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) p += sizeof(Atom); } assert(fixupOffset == _allFixups.size()); - _file->_fixups.reserve(fixupOffset); + _file->_fixups.resize(fixupOffset); // copy each fixup for each atom for(typename std::vector::iterator it=_allFixups.begin(); it != _allFixups.end(); ++it) { @@ -1871,6 +1954,13 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) return _file; } +static void versionToString(uint32_t value, char buffer[32]) +{ + if ( value & 0xFF ) + sprintf(buffer, "%d.%d.%d", value >> 16, (value >> 8) & 0xFF, value & 0xFF); + else + sprintf(buffer, "%d.%d", value >> 16, (value >> 8) & 0xFF); +} template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } @@ -1878,7 +1968,7 @@ template <> uint8_t Parser::loadCommandSizeMask() { return 0x03; } template <> uint8_t Parser::loadCommandSizeMask() { return 0x07; } template -bool Parser::parseLoadCommands() +bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOSVersion, bool simulator, bool ignoreMismatchPlatform) { const macho_header

* header = (const macho_header

*)_fileContent; @@ -1892,6 +1982,7 @@ bool Parser::parseLoadCommands() // an empty .o file with zero load commands will crash linker if ( cmd_count == 0 ) return false; + Options::Platform lcPlatform = Options::kPlatformUnknown; const macho_load_command

* const cmds = (macho_load_command

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

)); const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; @@ -1970,6 +2061,18 @@ bool Parser::parseLoadCommands() throw "LC_LINKER_OPTIMIZATION_HINTS table extends beyond end of file"; } break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_WATCHOS: + #if SUPPORT_APPLE_TV + case LC_VERSION_MIN_TVOS: + #endif + if ( ignoreMismatchPlatform ) + break; + _file->_platform = cmd->cmd(); + lcPlatform = Options::platformForLoadCommand(cmd->cmd()); + _file->_minOSVersion = ((macho_version_min_command

*)cmd)->version(); + break; default: if ( cmd->cmd() == macho_segment_command

::CMD ) { if ( segment != NULL ) @@ -1982,6 +2085,52 @@ bool Parser::parseLoadCommands() if ( cmd > cmdsEnd ) throwf("malformed mach-o file, load command #%d is outside size of load commands", i); } + // arm/arm64 objects are default to ios platform if not set. + // rdar://problem/21746314 + if (lcPlatform == Options::kPlatformUnknown && + (std::is_same::value || std::is_same::value)) + lcPlatform = Options::kPlatformiOS; + + // Check platform cross-linking. + if ( !ignoreMismatchPlatform ) { + if ( lcPlatform != platform ) { + switch (platform) { + case Options::kPlatformOSX: + case Options::kPlatformiOS: + if ( lcPlatform == Options::kPlatformUnknown ) + break; + // fall through if the Platform is not Unknown + case Options::kPlatformWatchOS: + // WatchOS errors on cross-linking all the time. + throwf("building for %s%s, but linking in object file built for %s,", + Options::platformName(platform), (simulator ? " simulator" : ""), + Options::platformName(lcPlatform)); + break; + #if SUPPORT_APPLE_TV + case Options::kPlatform_tvOS: + // tvOS is a warning temporarily. rdar://problem/21746965 + if (platform == Options::kPlatform_tvOS) + warning("URGENT: building for %s%s, but linking in object file (%s) built for %s. " + "Note: This will be an error in the future.", + Options::platformName(platform), (simulator ? " simulator" : ""), path(), + Options::platformName(lcPlatform)); + break; + #endif + case Options::kPlatformUnknown: + // skip if the target platform is unknown + break; + } + } + if ( linkMinOSVersion && (_file->_minOSVersion > linkMinOSVersion) ) { + char t1[32]; + char t2[32]; + versionToString(_file->_minOSVersion, t1); + versionToString(linkMinOSVersion, t2); + warning("object file (%s) was built for newer %s version (%s) than being linked (%s)", + _path, Options::platformName(lcPlatform), t1, t2); + } + } + // record range of sections if ( segment == NULL ) @@ -1992,6 +2141,35 @@ bool Parser::parseLoadCommands() return true; } +template +Options::Platform Parser::findPlatform(const macho_header

* header) +{ + const uint32_t cmd_count = header->ncmds(); + if ( cmd_count == 0 ) + return Options::kPlatformUnknown; + const macho_load_command

* const cmds = (macho_load_command

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

)); + const macho_load_command

* const cmdsEnd = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t size = cmd->cmdsize(); + if ( (size & loadCommandSizeMask()) != 0 ) + throwf("load command #%d has a unaligned size", i); + const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize(); + if ( endOfCmd > (uint8_t*)cmdsEnd ) + throwf("load command #%d extends beyond the end of the load commands", i); + switch (cmd->cmd()) { + case LC_VERSION_MIN_MACOSX: + return Options::kPlatformOSX; + case LC_VERSION_MIN_IPHONEOS: + return Options::kPlatformiOS; + } + cmd = (const macho_load_command

*)(((char*)cmd)+cmd->cmdsize()); + if ( cmd > cmdsEnd ) + throwf("malformed mach-o file, load command #%d is outside size of load commands", i); + } + return Options::kPlatformUnknown; +} + template void Parser::prescanSymbolTable() @@ -2258,6 +2436,13 @@ void Parser::makeSections() // allocate raw storage for all section objects on stack MachOSectionAndSectionClass

* machOSects = (MachOSectionAndSectionClass

*)machOSectsStorage; unsigned int count = 0; + // local variable for bitcode parsing + const macho_section

* bitcodeSect = NULL; + const macho_section

* cmdlineSect = NULL; + const macho_section

* swiftCmdlineSect = NULL; + const macho_section

* bundleSect = NULL; + bool bitcodeAsm = false; + for (uint32_t i=0; i < _machOSectionsCount; ++i) { const macho_section

* sect = &_sectionsStart[i]; if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) { @@ -2285,6 +2470,23 @@ void Parser::makeSections() } } } + if ( strcmp(sect->segname(), "__LLVM") == 0 ) { + if ( strncmp(sect->sectname(), "__bitcode", 9) == 0 ) { + bitcodeSect = sect; + } else if ( strncmp(sect->sectname(), "__cmdline", 9) == 0 ) { + cmdlineSect = sect; + } else if ( strncmp(sect->sectname(), "__swift_cmdline", 15) == 0 ) { + swiftCmdlineSect = sect; + } else if ( strncmp(sect->sectname(), "__bundle", 8) == 0 ) { + bundleSect = sect; + } else if ( strncmp(sect->sectname(), "__asm", 5) == 0 ) { + bitcodeAsm = true; + } + // If it is not single input for ld -r, don't count the section + // otherwise, fall through and add it to the sections. + if (_file->sourceKind() != ld::relocatable::File::kSourceSingle) + continue; + } // ignore empty __OBJC sections if ( (sect->size() == 0) && (strcmp(sect->segname(), "__OBJC") == 0) ) continue; @@ -2348,6 +2550,10 @@ void Parser::makeSections() totalSectionsSize += sizeof(NonLazyPointerSection); machOSects[count++].type = sectionTypeNonLazy; break; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + totalSectionsSize += sizeof(TLVPointerSection); + machOSects[count++].type = sectionTypeTLVPointers; + break; case S_LITERAL_POINTERS: if ( (strcmp(sect->segname(), "__OBJC") == 0) && (strcmp(sect->sectname(), "__cls_refs") == 0) ) { totalSectionsSize += sizeof(Objc1ClassReferences); @@ -2404,11 +2610,26 @@ void Parser::makeSections() totalSectionsSize += sizeof(TLVDefsSection); machOSects[count++].type = sectionTypeTLVDefs; break; - case S_THREAD_LOCAL_VARIABLE_POINTERS: default: throwf("unknown section type %d", sect->flags() & SECTION_TYPE); } } + + // Create bitcode + if ( bitcodeSect != NULL ) { + if ( cmdlineSect != NULL ) + _file->_bitcode = std::unique_ptr(new ld::ClangBitcode(&_fileContent[bitcodeSect->offset()], bitcodeSect->size(), + &_fileContent[cmdlineSect->offset()], cmdlineSect->size())); + else if ( swiftCmdlineSect != NULL ) + _file->_bitcode = std::unique_ptr(new ld::SwiftBitcode(&_fileContent[bitcodeSect->offset()], bitcodeSect->size(), + &_fileContent[swiftCmdlineSect->offset()], swiftCmdlineSect->size())); + else + throwf("Object file with bitcode missing cmdline options: %s", _file->path()); + } + else if ( bundleSect != NULL ) + _file->_bitcode = std::unique_ptr(new ld::BundleBitcode(&_fileContent[bundleSect->offset()], bundleSect->size())); + else if ( bitcodeAsm ) + _file->_bitcode = std::unique_ptr(new ld::AsmBitcode(_fileContent, _fileLength)); // sort by address (mach-o object files don't aways have sections sorted) ::qsort(machOSects, count, sizeof(MachOSectionAndSectionClass

), MachOSectionAndSectionClass

::sorter); @@ -2451,6 +2672,10 @@ void Parser::makeSections() *objects++ = new (space) NonLazyPointerSection(*this, *_file, machOSects[i].sect); space += sizeof(NonLazyPointerSection); break; + case sectionTypeTLVPointers: + *objects++ = new (space) TLVPointerSection(*this, *_file, machOSects[i].sect); + space += sizeof(TLVPointerSection); + break; case sectionTypeCFI: _EHFrameSection = new (space) CFISection(*this, *_file, machOSects[i].sect); *objects++ = _EHFrameSection; @@ -3481,7 +3706,7 @@ void Parser::parseDebugInfo() p += sizeof(Atom); } assert(liOffset == entries.size()); - _file->_lineInfos.reserve(liOffset); + _file->_lineInfos.resize(liOffset); // copy each line info for each atom for (typename std::vector >::iterator it = entries.begin(); it != entries.end(); ++it) { @@ -3853,8 +4078,6 @@ const char* File::translationUnitSource() const return _dwarfTranslationUnitPath; } - - template bool File::forEachAtom(ld::File::AtomHandler& handler) const { @@ -4014,6 +4237,8 @@ ld::Section::Type Section::sectionType(const macho_section* se return ld::Section::typeTLVZeroFill; case S_THREAD_LOCAL_VARIABLES: return ld::Section::typeTLVDefs; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + return ld::Section::typeTLVPointers; case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: return ld::Section::typeTLVInitializerPointers; } @@ -4728,6 +4953,7 @@ const char* CUSection::personalityName(class Parser& parser, const const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address()); pint_t personalityAddr = *content; Section* personalitySection = parser.sectionForAddress(personalityAddr); + (void)personalitySection; assert((personalitySection->type() == ld::Section::typeCode) && "personality column in __compact_unwind section is not pointer to function"); // atoms may not be constructed yet, so scan symbol table for labels const char* name = parser.scanSymbolTableForAddress(personalityAddr); @@ -5468,6 +5694,73 @@ ld::Atom::Scope NonLazyPointerSection::scopeAtAddress(Parser& parser, pint return ld::Atom::scopeLinkageUnit; } + + +template +ld::Atom::Combine TLVPointerSection::combine(Parser& parser, pint_t addr) +{ + return ld::Atom::combineByNameAndReferences; +} + + +template +const char* TLVPointerSection::targetName(const class Atom* atom, const ld::IndirectBindingTable& ind, bool* isStatic) +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + assert(atom->fixupCount() == 1); + *isStatic = false; + ld::Fixup::iterator fit = atom->fixupsBegin(); + const char* name = NULL; + switch ( fit->binding ) { + case ld::Fixup::bindingByNameUnbound: + name = fit->u.name; + break; + case ld::Fixup::bindingByContentBound: + name = fit->u.target->name(); + break; + case ld::Fixup::bindingsIndirectlyBound: + name = ind.indirectName(fit->u.bindingIndex); + break; + case ld::Fixup::bindingDirectlyBound: + name = fit->u.target->name(); + *isStatic = (fit->u.target->scope() == ld::Atom::scopeTranslationUnit); + break; + default: + assert(0); + } + assert(name != NULL); + return name; +} + +template +unsigned long TLVPointerSection::contentHash(const class Atom* atom, const ld::IndirectBindingTable& ind) const +{ + assert(atom->combine() == ld::Atom::combineByNameAndReferences); + unsigned long hash = 9508; + bool isStatic; + for (const char* s = this->targetName(atom, ind, &isStatic); *s != '\0'; ++s) { + hash = hash * 33 + *s; + } + return hash; +} + +template +bool TLVPointerSection::canCoalesceWith(const class Atom* atom, const ld::Atom& rhs, + const ld::IndirectBindingTable& indirectBindingTable) const +{ + if ( rhs.section().type() != ld::Section::typeTLVPointers ) + return false; + assert(this->type() == rhs.section().type()); + const Atom* rhsAtom = dynamic_cast*>(&rhs); + assert(rhsAtom != NULL); + bool thisIsStatic; + bool rhsIsStatic; + const char* thisName = this->targetName(atom, indirectBindingTable, &thisIsStatic); + const char* rhsName = this->targetName(rhsAtom, indirectBindingTable, &rhsIsStatic); + return !thisIsStatic && !rhsIsStatic && (strcmp(thisName, rhsName) == 0); +} + + template const uint8_t* CFStringSection::targetContent(const class Atom* atom, const ld::IndirectBindingTable& ind, ContentType* ct, unsigned int* count) @@ -6826,7 +7119,8 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relo case ARM64_RELOC_UNSIGNED: if ( reloc->r_pcrel() ) throw "pcrel and ARM64_RELOC_UNSIGNED not supported"; - target.addend = contentValue; + if ( reloc->r_extern() ) + target.addend = contentValue; switch ( reloc->r_length() ) { case 0: case 1: @@ -7451,28 +7745,34 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserO // // used by linker to infer architecture when no -arch is on command line // -bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult) +bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, Options::Platform* platform) { if ( mach_o::relocatable::Parser::validFile(fileContent) ) { *result = CPU_TYPE_X86_64; const macho_header >* header = (const macho_header >*)fileContent; *subResult = header->cpusubtype(); + *platform = Parser::findPlatform(header); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + const macho_header >* header = (const macho_header >*)fileContent; *result = CPU_TYPE_I386; *subResult = CPU_SUBTYPE_X86_ALL; + *platform = Parser::findPlatform(header); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { - *result = CPU_TYPE_ARM; const macho_header >* header = (const macho_header >*)fileContent; + *result = CPU_TYPE_ARM; *subResult = header->cpusubtype(); + *platform = Parser::findPlatform(header); return true; } if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + const macho_header >* header = (const macho_header >*)fileContent; *result = CPU_TYPE_ARM64; *subResult = CPU_SUBTYPE_ARM64_ALL; + *platform = Parser::findPlatform(header); return true; } return false; @@ -7528,6 +7828,26 @@ bool hasObjC1Categories(const uint8_t* fileContent) return false; } +// +// Used by bitcode obfuscator to get a list of non local symbols from object file +// +bool getNonLocalSymbols(const uint8_t* fileContent, std::vector &syms) +{ + if ( mach_o::relocatable::Parser::validFile(fileContent) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + else if ( mach_o::relocatable::Parser::validFile(fileContent, false, 0) ) { + return mach_o::relocatable::Parser::getNonLocalSymbols(fileContent, syms); + } + return false; +} + } // namespace relocatable diff --git a/src/ld/parsers/macho_relocatable_file.h b/src/ld/parsers/macho_relocatable_file.h index 92e9042..40f02d6 100644 --- a/src/ld/parsers/macho_relocatable_file.h +++ b/src/ld/parsers/macho_relocatable_file.h @@ -41,7 +41,12 @@ struct ParserOptions { bool neverConvertDwarf; bool verboseOptimizationHints; bool armUsesZeroCostExceptions; + bool simulator; + bool ignoreMismatchPlatform; uint32_t subType; + Options::Platform platform; + uint32_t minOSVersion; + ld::relocatable::File::SourceKind srcKind; }; extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, @@ -50,13 +55,15 @@ extern ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLen extern bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, const ParserOptions& opts); -extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult); +extern bool isObjectFile(const uint8_t* fileContent, cpu_type_t* result, cpu_subtype_t* subResult, Options::Platform* platform); extern bool hasObjC2Categories(const uint8_t* fileContent); extern bool hasObjC1Categories(const uint8_t* fileContent); -extern const char* archName(const uint8_t* fileContent); +extern const char* archName(const uint8_t* fileContent); + +bool getNonLocalSymbols(const uint8_t* fileContent, std::vector &syms); } // namespace relocatable } // namespace mach_o diff --git a/src/ld/parsers/opaque_section_file.cpp b/src/ld/parsers/opaque_section_file.cpp index e60332b..081e957 100644 --- a/src/ld/parsers/opaque_section_file.cpp +++ b/src/ld/parsers/opaque_section_file.cpp @@ -24,6 +24,7 @@ #include +#include #include "ld.hpp" #include "opaque_section_file.h" @@ -63,7 +64,7 @@ public: const char* symbolName="sect_create") : ld::File(pth, 0, ld::File::Ordinal::NullOrdinal(), Other), _atom(*this, symbolName, fileContent, fileLength), - _section(segmentName, sectionName, ld::Section::typeUnclassified) { } + _section(segmentName, sectionName, ld::Section::typeSectCreate) { } virtual ~File() { } virtual bool forEachAtom(ld::File::AtomHandler& h) const { h.doAtom(_atom); return true; } diff --git a/src/ld/parsers/textstub_dylib_file.cpp b/src/ld/parsers/textstub_dylib_file.cpp new file mode 100644 index 0000000..cde3344 --- /dev/null +++ b/src/ld/parsers/textstub_dylib_file.cpp @@ -0,0 +1,1072 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include + +#include + +#include "Architectures.hpp" +#include "bitcode.hpp" +#include "MachOFileAbstraction.hpp" +#include "MachOTrie.hpp" +#include "textstub_dylib_file.hpp" + +namespace { + +/// +/// A token is a light-weight reference to the content of an nmap'ed file. It +/// doesn't own the data and it doesn't make a copy of it. The referenced data +/// is only valid as long as the file is mapped in. +/// +class Token { + const char* _p; + size_t _size; + + int compareMemory(const char* lhs, const char* rhs, size_t size) const { + if (size == 0) + return 0; + return ::memcmp(lhs, rhs, size); + } + +public: + Token() : _p(nullptr), _size(0) {} + + Token(const char* p) : _p(p), _size(0) { + if (p) + _size = ::strlen(p); + } + + Token(const char* p, size_t s) : _p(p), _size(s) {} + + const char* data() const { return _p; } + + size_t size() const { return _size; } + + std::string str() const { return std::move(std::string(_p, _size)); } + + bool empty() const { return _size == 0; } + + bool operator==(Token other) const { + if (_size != other._size) + return false; + return compareMemory(_p, other._p, _size) == 0; + } + + bool operator!=(Token other) const { + return !(*this == other); + } +}; + +/// +/// Simple text-based dynamic library file tokenizer. +/// +class Tokenizer { + const char* _start; + const char* _current; + const char* _end; + Token _currentToken; + + void fetchNextToken(); + void scanToNextToken(); + void skip(unsigned distance) { + _current += distance; + assert(_current <= _end && "Skipped past the end"); + } + + const char* skipLineBreak(const char* pos) const; + bool isDelimiter(const char* pos) const; + +public: + Tokenizer(const char* data, uint64_t size) : _start(data), _current(data), _end(data + size) {} + + void reset() { + _current = _start; + fetchNextToken(); + } + + Token peek() { return _currentToken; } + Token next() { + Token token = peek(); + fetchNextToken(); + return token; + } +}; + +const char* Tokenizer::skipLineBreak(const char* pos) const +{ + if ( pos == _end ) + return pos; + + // Carriage return. + if ( *pos == 0x0D ) { + // line feed. + if ( pos + 1 != _end && *(pos + 1) == 0x0A) + return pos + 2; + return pos + 1; + } + + // line feed. + if ( *pos == 0x0A ) + return pos + 1; + + return pos; +} + +void Tokenizer::scanToNextToken() { + while (true) { + while ( isDelimiter(_current) ) + skip(1); + + const char* i = skipLineBreak(_current); + if ( i == _current ) + break; + + _current = i; + } +} + + +bool Tokenizer::isDelimiter(const char* pos) const { + if ( pos == _end ) + return false; + if ( *pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n' || *pos == ',' || *pos == ':' || *pos == '\'' || *pos == '\"' ) + return true; + return false; +} + +void Tokenizer::fetchNextToken() { + scanToNextToken(); + + if (_current == _end) { + _currentToken = Token(); + return; + } + + auto start = _current; + while ( !isDelimiter(_current) ) { + ++_current; + } + + _currentToken = Token(start, _current - start); +} + +/// +/// Representation of a parsed text-based dynamic library file. +/// +struct DynamicLibrary { + Token _installName; + uint32_t _currentVersion; + uint32_t _compatibilityVersion; + uint8_t _swiftVersion; + ld::File::ObjcConstraint _objcConstraint; + Options::Platform _platform; + std::vector _allowedClients; + std::vector _reexportedLibraries; + std::vector _symbols; + std::vector _classes; + std::vector _ivars; + std::vector _weakDefSymbols; + std::vector _tlvSymbols; + + DynamicLibrary() : _currentVersion(0x10000), _compatibilityVersion(0x10000), _swiftVersion(0), + _objcConstraint(ld::File::objcConstraintNone) {} +}; + +static uint32_t parseVersionNumber32(Token token) { + if ( token.size() >= 128 ) + throwf("malformed version number"); + + char buffer[128]; + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + char* end; + + // Make a null-terminated string. + ::memcpy(buffer, token.data(), token.size()); + buffer[token.size()] = '\0'; + + x = strtoul(buffer, &end, 10); + if ( *end == '.' ) { + y = strtoul(&end[1], &end, 10); + if ( *end == '.' ) { + z = strtoul(&end[1], &end, 10); + } + } + if ( (x > 0xffff) || (y > 0xff) || (z > 0xff) ) + throwf("malformed 32-bit x.y.z version number: %s", buffer); + + return (x << 16) | ( y << 8 ) | z; +} + +/// +/// A simple text-based dynamic library file parser. +/// +class TBDFile { + Tokenizer _tokenizer; + + Token peek() { return _tokenizer.peek(); } + Token next() { return _tokenizer.next(); } + + void expectToken(Token str) { + Token token = next(); + if (token != str) + throwf("unexpected token: %s", token.str().c_str()); + } + + bool hasOptionalToken(Token str) { + auto token = peek(); + if ( token == str ) { + next(); + return true; + } + return false; + } + + + void parseFlowSequence(std::function func) { + expectToken("["); + + while ( true ) { + auto token = peek(); + if ( token == "]" ) + break; + + token = next(); + func(token); + } + + expectToken("]"); + } + + void parseAllowedClients(DynamicLibrary& lib) { + if ( !hasOptionalToken("allowed-clients") ) + return; + parseFlowSequence([&](Token name) { + lib._allowedClients.emplace_back(name); + }); + } + + void parseReexportedDylibs(DynamicLibrary& lib) { + if ( !hasOptionalToken("re-exports") ) + return; + parseFlowSequence([&](Token name) { + lib._reexportedLibraries.emplace_back(name); + }); + } + + void parseSymbols(DynamicLibrary& lib) { + if ( hasOptionalToken("symbols") ) { + parseFlowSequence([&](Token name) { + lib._symbols.emplace_back(name); + }); + } + + if ( hasOptionalToken("objc-classes") ) { + parseFlowSequence([&](Token name) { + lib._classes.emplace_back(name); + }); + } + + if ( hasOptionalToken("objc-ivars") ) { + parseFlowSequence([&](Token name) { + lib._ivars.emplace_back(name); + }); + } + + if ( hasOptionalToken("weak-def-symbols") ) { + parseFlowSequence([&](Token name) { + lib._weakDefSymbols.emplace_back(name); + }); + } + + if ( hasOptionalToken("thread-local-symbols") ) { + parseFlowSequence([&](Token name) { + lib._tlvSymbols.emplace_back(name); + }); + } + } + + bool parseArchFlowSequence(Token archName) { + expectToken("archs"); + + bool foundArch = false; + parseFlowSequence([&](Token name) { + if ( name == archName ) + foundArch = true; + }); + + return foundArch; + } + + void parsePlatform(DynamicLibrary& lib) { + expectToken("platform"); + + auto token = next(); + if (token == "macosx") + lib._platform = Options::kPlatformOSX; + else if (token == "ios") + lib._platform = Options::kPlatformiOS; + else if (token == "watchos") + lib._platform = Options::kPlatformWatchOS; +#if SUPPORT_APPLE_TV + else if (token == "tvos") + lib._platform = Options::kPlatform_tvOS; +#endif + else + lib._platform = Options::kPlatformUnknown; + } + + void parseInstallName(DynamicLibrary& lib) { + expectToken("install-name"); + + lib._installName = next(); + if ( lib._installName.empty() ) + throwf("no install name specified"); + } + + void parseCurrentVersion(DynamicLibrary& lib) { + if ( !hasOptionalToken("current-version") ) + return; + lib._currentVersion = parseVersionNumber32(next()); + } + + void parseCompatibilityVersion(DynamicLibrary& lib) { + if ( !hasOptionalToken("compatibility-version") ) + return; + lib._compatibilityVersion = parseVersionNumber32(next()); + } + + void parseSwiftVersion(DynamicLibrary& lib) { + if ( !hasOptionalToken("swift-version") ) + return; + auto token = next(); + if ( token == "1.0" ) + lib._swiftVersion = 1; + else if ( token == "1.1" ) + lib._swiftVersion = 2; + else if ( token == "2.0" ) + lib._swiftVersion = 3; + else + throwf("unsupported Swift ABI version: %s", token.str().c_str()); + } + + void parseObjCConstraint(DynamicLibrary& lib) { + if ( !hasOptionalToken("objc-constraint") ) + return; + auto token = next(); + if ( token == "none" ) + lib._objcConstraint = ld::File::objcConstraintNone; + else if ( token == "retain_release" ) + lib._objcConstraint = ld::File::objcConstraintRetainRelease; + else if ( token == "retain_release_for_simulator" ) + lib._objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator; + else if ( token == "retain_release_or_gc" ) + lib._objcConstraint = ld::File::objcConstraintRetainReleaseOrGC; + else if ( token == "gc" ) + lib._objcConstraint = ld::File::objcConstraintGC; + else + throwf("unexpected token: %s", token.str().c_str()); + } + void parseExportsBlock(DynamicLibrary& lib, Token archName) { + if ( !hasOptionalToken("exports") ) + return; + + if ( !hasOptionalToken("-") ) + return; + + while ( true ) { + if ( !parseArchFlowSequence(archName) ) { + Token token; + while ( true ) { + token = peek(); + if ( token == "archs" || token == "..." || token.empty() ) + break; + next(); + } + if (token == "..." || token.empty() ) + break; + + continue; + } + + parseAllowedClients(lib); + parseReexportedDylibs(lib); + parseSymbols(lib); + if ( !hasOptionalToken("-") ) + break; + } + } + + void parseDocument(DynamicLibrary& lib, Token archName) { + if ( !parseArchFlowSequence(archName) ) + throwf("invalid arch"); + + parsePlatform(lib); + parseInstallName(lib); + parseCurrentVersion(lib); + parseCompatibilityVersion(lib); + parseSwiftVersion(lib); + parseObjCConstraint(lib); + parseExportsBlock(lib, archName); + } + +public: + TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {} + + DynamicLibrary parseFileForArch(Token archName) { + _tokenizer.reset(); + DynamicLibrary lib; + expectToken("---"); + parseDocument(lib, archName); + expectToken("..."); + return std::move(lib); + } + + bool validForArch(Token archName) { + _tokenizer.reset(); + auto token = next(); + if ( token != "---" ) + return false; + return parseArchFlowSequence(archName); + } + + void dumpTokens() { + _tokenizer.reset(); + Token token; + do { + token = next(); + printf("token: %s\n", token.str().c_str()); + } while ( !token.empty() ); + } +}; + +} // end anonymous namespace + +namespace textstub { +namespace dylib { + +// forward reference +template class File; + + +// +// An ExportAtom has no content. It exists so that the linker can track which imported +// symbols came from which dynamic libraries. +// +template +class ExportAtom : public ld::Atom +{ +public: + ExportAtom(const File& f, const char* nm, bool weakDef, bool tlv) + : ld::Atom(f._importProxySection, ld::Atom::definitionProxy, + (weakDef? ld::Atom::combineByName : ld::Atom::combineNever), + ld::Atom::scopeLinkageUnit, + (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified), + symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), + _file(f), _name(nm) {} + // overrides of ld::Atom + virtual const ld::File* file() const { return &_file; } + virtual const char* name() const { return _name; } + virtual uint64_t size() const { return 0; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual void setScope(Scope) { } + +protected: + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + virtual ~ExportAtom() {} + + const File& _file; + const char* _name; +}; + + +// +// The reader for a dylib extracts all exported symbols names from the memory-mapped +// dylib, builds a hash table, then unmaps the file. This is an important memory +// savings for large dylibs. +// +template +class File : public ld::dylib::File +{ +public: + static bool validFile(const uint8_t* fileContent, bool executableOrDylib); + File(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, + bool hoistImplicitPublicDylibs, Options::Platform platform, + cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* installPath, bool indirectDylib); + virtual ~File() {} + + // overrides of ld::File + virtual bool forEachAtom(ld::File::AtomHandler&) const; + virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; + virtual ld::File::ObjcConstraint objCConstraint() const { return _objcConstraint; } + virtual uint8_t swiftVersion() const { return _swiftVersion; } + + // overrides of ld::dylib::File + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool); + virtual bool providedExportAtom() const { return _providedAtom; } + virtual const char* parentUmbrella() const { return nullptr; } + virtual const std::vector* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : nullptr; } + virtual bool hasWeakExternals() const { return _hasWeakExports; } + virtual bool deadStrippable() const { return false; } + virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; } + virtual bool hasWeakDefinition(const char* name) const; + virtual bool allSymbolsAreWeakImported() const; + virtual bool installPathVersionSpecific() const { return _installPathOverride; } + // All text-based stubs are per definition AppExtensionSafe. + virtual bool appExtensionSafe() const { return true; }; + virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } + + +protected: + virtual void assertNoReExportCycles(ReExportChain*) const; + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + friend class ExportAtom; + + struct CStringHash { + std::size_t operator()(const char* __s) const { + unsigned long __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return size_t(__h); + }; + }; + struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; }; + typedef std::unordered_map NameToAtomMap; + typedef std::unordered_set NameSet; + + struct Dependent { const char* path; File* dylib; }; + + virtual std::pair hasWeakDefinitionImpl(const char* name) const; + virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& address) const; + + void buildExportHashTable(const DynamicLibrary &lib); + bool isPublicLocation(const char* pth); + bool wrongOS() { return _wrongOS; } + void addSymbol(const char* name, bool weak, bool tlv); + + const Options::Platform _platform; + cpu_type_t _cpuType; + const uint32_t _linkMinOSVersion; + const bool _allowSimToMacOSXLinking; + const bool _addVersionLoadCommand; + bool _linkingFlat; + bool _implicitlyLinkPublicDylibs; + ld::File::ObjcConstraint _objcConstraint; + uint8_t _swiftVersion; + ld::Section _importProxySection; + ld::Section _flatDummySection; + std::vector _dependentDylibs; + std::vector _allowableClients; + mutable NameToAtomMap _atoms; + NameSet _ignoreExports; + bool _noRexports; + bool _hasWeakExports; + bool _hasPublicInstallName; + mutable bool _providedAtom; + bool _wrongOS; + bool _installPathOverride; + bool _indirectDylibsProcessed; + std::unique_ptr _bitcode; + static bool _s_logHashtable; +}; + +template +bool File::_s_logHashtable = false; + + +template +File::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime, + ld::File::Ordinal ord, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs, + Options::Platform platform, cpu_type_t cpuType, const char* archName, + uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers, + bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath, + bool indirectDylib) + : ld::dylib::File(strdup(path), mTime, ord), _platform(platform), _cpuType(cpuType), + _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX), + _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace), + _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs), + _objcConstraint(ld::File::objcConstraintNone), _swiftVersion(0), + _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true), + _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true), + _noRexports(false), _hasWeakExports(false), + _hasPublicInstallName(false), _providedAtom(false), _wrongOS(false), + _installPathOverride(false), _indirectDylibsProcessed(false), + _bitcode(new ld::Bitcode(nullptr, 0)) +{ + // write out path for -t option + if ( logAllFiles ) + printf("%s\n", path); + + TBDFile stub((const char*)fileContent, fileLength); + auto lib = stub.parseFileForArch(archName); + + _noRexports = lib._reexportedLibraries.empty(); + _hasWeakExports = !lib._weakDefSymbols.empty(); + _dylibInstallPath = strdup(lib._installName.str().c_str()); + _dylibCurrentVersion = lib._currentVersion; + _dylibCompatibilityVersion = lib._compatibilityVersion; + _swiftVersion = lib._swiftVersion; + _objcConstraint = lib._objcConstraint; + _hasPublicInstallName = isPublicLocation(_dylibInstallPath); + + for (auto &client : lib._allowedClients) + _allowableClients.push_back(strdup(client.str().c_str())); + + // [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked + if ( !_allowableClients.empty() ) + _hasPublicInstallName = false; + + if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) { + _wrongOS = true; + if ( _addVersionLoadCommand && !indirectDylib ) { + if ( buildingForSimulator ) { + if ( !_allowSimToMacOSXLinking ) + throwf("building for %s simulator, but linking against dylib built for %s (%s).", + Options::platformName(platform), Options::platformName(lib._platform), path); + } else { + throwf("building for %s, but linking against dylib built for %s (%s).", + Options::platformName(platform), Options::platformName(lib._platform), path); + } + } + } + + _dependentDylibs.reserve(lib._reexportedLibraries.size()); + for ( auto& reexport : lib._reexportedLibraries ) { + Dependent entry; + entry.path = strdup(reexport.str().c_str()); + entry.dylib = nullptr; + if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, entry.path) != 0) ) + _dependentDylibs.push_back(entry); + } + + // build hash table + buildExportHashTable(lib); + + munmap((caddr_t)fileContent, fileLength); +} + +template +void File::buildExportHashTable(const DynamicLibrary& lib) { + if ( _s_logHashtable ) + fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path()); + + for (auto &sym : lib._symbols) + addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/false); + +#if SUPPORT_ARCH_i386 + if (_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) { + for (auto &sym : lib._classes) + addSymbol((".objc_class_name" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + } else { + for (auto &sym : lib._classes) { + addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + } + } +#else + for (auto &sym : lib._classes) { + addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + } +#endif + + for (auto &sym : lib._ivars) + addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false); + + for (auto &sym : lib._weakDefSymbols) + addSymbol(sym.str().c_str(), /*weak=*/true, /*tlv=*/false); + + for (auto &sym : lib._tlvSymbols) + addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true); +} + + +template +void File::addSymbol(const char* name, bool weakDef, bool tlv) +{ + // symbols that start with $ld$ are meta-data to the static linker + // need way for ld and dyld to see different exported symbols in a dylib + if ( strncmp(name, "$ld$", 4) == 0 ) { + // $ld$ $ $ + const char* symAction = &name[4]; + const char* symCond = strchr(symAction, '$'); + if ( symCond != nullptr ) { + char curOSVers[16]; + sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF)); + if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) { + const char* symName = strchr(&symCond[1], '$'); + if ( symName != nullptr ) { + ++symName; + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( _s_logHashtable ) + fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path()); + _ignoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef, false); + return; + } + else if ( strncmp(symAction, "install_name$", 13) == 0 ) { + _dylibInstallPath = strdup(symName); + _installPathOverride = true; + return; + } + else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) { + _dylibCompatibilityVersion = parseVersionNumber32(symName); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->path()); + } + } + } + } + else { + warning("bad symbol condition: %s in dylib %s", name, this->path()); + } + } + + // add symbol as possible export if we are not supposed to ignore it + if ( _ignoreExports.count(name) == 0 ) { + AtomAndWeak bucket; + bucket.atom = nullptr; + bucket.weakDef = weakDef; + bucket.tlv = tlv; + if ( _s_logHashtable ) + fprintf(stderr, " adding %s to hash table for %s\n", name, this->path()); + _atoms[strdup(name)] = bucket; + } +} + + +template +bool File::forEachAtom(ld::File::AtomHandler& handler) const +{ + handler.doFile(*this); + return false; +} + + +template +std::pair File::hasWeakDefinitionImpl(const char* name) const +{ + const auto pos = _atoms.find(name); + if ( pos != _atoms.end() ) + return std::make_pair(true, pos->second.weakDef); + + // look in children that I re-export + for (const auto &dep : _dependentDylibs) { + auto ret = dep.dylib->hasWeakDefinitionImpl(name); + if ( ret.first ) + return ret; + } + return std::make_pair(false, false); +} + + +template +bool File::hasWeakDefinition(const char* name) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + return hasWeakDefinitionImpl(name).second; +} + + +// If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB +template +bool File::allSymbolsAreWeakImported() const +{ + bool foundNonWeakImport = false; + bool foundWeakImport = false; + for (const auto &it : _atoms) { + const ld::Atom* atom = it.second.atom; + if ( atom != nullptr ) { + if ( atom->weakImported() ) + foundWeakImport = true; + else + foundNonWeakImport = true; + } + } + + // don't automatically weak link dylib with no imports + // so at least one weak import symbol and no non-weak-imported symbols must be found + return foundWeakImport && !foundNonWeakImport; +} + + +template +bool File::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& addr) const +{ + if ( _ignoreExports.count(name) != 0 ) + return false; + + // check myself + const auto pos = _atoms.find(name); + if ( pos != _atoms.end() ) { + weakDef = pos->second.weakDef; + tlv = pos->second.tlv; + addr = 0; + return true; + } + + // check dylibs I re-export + for (const auto& lib : _dependentDylibs) { + if ( !lib.dylib->implicitlyLinked() ) { + if ( lib.dylib->containsOrReExports(name, weakDef, tlv, addr) ) + return true; + } + } + + return false; +} + + +template +bool File::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const +{ + // if supposed to ignore this export, then pretend I don't have it + if ( _ignoreExports.count(name) != 0 ) + return false; + + + AtomAndWeak bucket; + uint64_t addr; + if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, addr) ) { + bucket.atom = new ExportAtom(*this, name, bucket.weakDef, bucket.tlv); + _atoms[name] = bucket; + _providedAtom = true; + if ( _s_logHashtable ) + fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path()); + // call handler with new export atom + handler.doAtom(*bucket.atom); + return true; + } + + return false; +} + + + +template +bool File::isPublicLocation(const char* path) +{ + // -no_implicit_dylibs disables this optimization + if ( ! _implicitlyLinkPublicDylibs ) + return false; + + // /usr/lib is a public location + if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&path[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != nullptr ) { + int frameworkNameLen = frameworkDot - &path[27]; + if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} + +template +void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) +{ + // only do this once + if ( _indirectDylibsProcessed ) + return; + + const static bool log = false; + if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath()); + if ( _linkingFlat ) { + for (auto& lib : _dependentDylibs) { + lib.dylib = (File*)handler->findDylib(lib.path, this->path()); + } + } + else if ( _noRexports ) { + // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do + } + else { + // two-level, might have re-exports + for (auto& lib : _dependentDylibs) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), lib.path); + // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child + lib.dylib = (File*)handler->findDylib(lib.path, this->path()); + if ( lib.dylib->hasPublicInstallName() && !lib.dylib->wrongOS() ) { + // promote this child to be automatically added as a direct dependent if this already is + if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(lib.path, lib.dylib->installPath()) == 0) ) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", lib.dylib->installPath()); + lib.dylib->setImplicitlyLinked(); + } + else if ( lib.dylib->explicitlyLinked() || lib.dylib->implicitlyLinked() ) { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n"); + } else { + if ( log ) + fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib.path); + } + } else { + // add all child's symbols to me + if ( log ) + fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib.path); + } + } + } + + // check for re-export cycles + ReExportChain chain; + chain.prev = nullptr; + chain.file = this; + this->assertNoReExportCycles(&chain); + + _indirectDylibsProcessed = true; +} + +template +void File::assertNoReExportCycles(ReExportChain* prev) const +{ + // recursively check my re-exported dylibs + ReExportChain chain; + chain.prev = prev; + chain.file = this; + for (const auto& dep : _dependentDylibs) { + ld::File* child = dep.dylib; + // check child is not already in chain + for (ReExportChain* p = prev; p != nullptr; p = p->prev) { + if ( p->file == child ) + throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path()); + } + if ( dep.dylib != nullptr ) + dep.dylib->assertNoReExportCycles(&chain); + } +} + + +template +class Parser +{ +public: + typedef typename A::P P; + + static bool validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName); + static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t mTime, ld::File::Ordinal ordinal, const Options& opts, + bool indirectDylib) { + return new File(fileContent, fileLength, path, mTime, ordinal, + opts.flatNamespace(), + opts.implicitlyLinkIndirectPublicDylibs(), + opts.platform(), + opts.architecture(), + opts.architectureName(), + opts.minOSversion(), + opts.allowSimulatorToLinkWithMacOSX(), + opts.addVersionLoadCommand(), + opts.targetIOSSimulator(), + opts.logAllFiles(), + opts.installPath(), + indirectDylib); + } +}; + +template +bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName) +{ + if ( path.find(".tbd", path.size()-4) == std::string::npos ) + return false; + + TBDFile stub((const char*)fileContent, fileLength); + if ( !stub.validForArch(archName) ) + throwf("missing required architecture %s in file %s", archName, path.c_str()); + + return true; +} + +// +// main function used by linker to instantiate ld::Files +// +ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const Options& opts, ld::File::Ordinal ordinal, + bool bundleLoader, bool indirectDylib) +{ + switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 + case CPU_TYPE_X86_64: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif +#if SUPPORT_ARCH_i386 + case CPU_TYPE_I386: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif +#if SUPPORT_ARCH_arm_any + case CPU_TYPE_ARM: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + if ( Parser::validFile(fileContent, fileLength, path, opts.architectureName()) ) + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib); + break; +#endif + } + return nullptr; +} + + +} // namespace dylib +} // namespace textstub + + diff --git a/src/ld/parsers/textstub_dylib_file.hpp b/src/ld/parsers/textstub_dylib_file.hpp new file mode 100644 index 0000000..e0a75f6 --- /dev/null +++ b/src/ld/parsers/textstub_dylib_file.hpp @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __TEXTSTUB_DYLIB_FILE_H__ +#define __TEXTSTUB_DYLIB_FILE_H__ + +#include "ld.hpp" +#include "Options.h" + +namespace textstub { +namespace dylib { + + +extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, + time_t modTime, const Options& opts, ld::File::Ordinal ordinal, + bool bundleLoader, bool indirectDylib); + +} // namespace dylib +} // namespace textstub + + +#endif // __TEXTSTUB_DYLIB_FILE_H__ diff --git a/src/ld/passes/bitcode_bundle.cpp b/src/ld/passes/bitcode_bundle.cpp new file mode 100644 index 0000000..bafad0b --- /dev/null +++ b/src/ld/passes/bitcode_bundle.cpp @@ -0,0 +1,809 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llvm-c/lto.h" +// c header +extern "C" { +#include +} + +#include "bitcode_bundle.h" + +#include "Options.h" +#include "ld.hpp" +#include "Bitcode.hpp" +#include "macho_relocatable_file.h" + + +namespace ld { +namespace passes { +namespace bitcode_bundle { + +class BitcodeTempFile; + +class BitcodeAtom : public ld::Atom { + static ld::Section bitcodeBundleSection; +public: + BitcodeAtom(); + BitcodeAtom(BitcodeTempFile& tempfile); + ~BitcodeAtom() { free(_content); } + virtual ld::File* file() const { return NULL; } + virtual const char* name() const { return "bitcode bundle"; } + virtual uint64_t size() const { return _size; } + virtual uint64_t objectAddress() const { return 0; } + virtual void copyRawContent(uint8_t buffer[]) const + { memcpy(buffer, _content, _size); } + virtual void setScope(Scope) { } + +private: + uint8_t* _content; + uint64_t _size; +}; + +ld::Section BitcodeAtom::bitcodeBundleSection("__LLVM", "__bundle", ld::Section::typeSectCreate); + +class BitcodeTempFile { +public: + BitcodeTempFile(const char* path, bool deleteAfterRead); + ~BitcodeTempFile(); + uint8_t* getContent() const { return _content; } + uint64_t getSize() const { return _size; } +private: + friend class BitcodeAtom; + const char* _path; + uint8_t* _content; + uint64_t _size; + bool _deleteAfterRead; +}; + +class BitcodeObfuscator { +public: + BitcodeObfuscator(); + ~BitcodeObfuscator(); + + void addMustPreserveSymbols(const char* name); + void bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath); + void writeSymbolMap(const char* outputPath); +private: + typedef void (*lto_codegen_func_t) (lto_code_gen_t); + typedef void (*lto_codegen_output_t) (lto_code_gen_t, const char*); + + lto_code_gen_t _obfuscator; + lto_codegen_func_t _lto_hide_symbols; + lto_codegen_func_t _lto_reset_context; + lto_codegen_output_t _lto_write_reverse_map; +}; + +class FileHandler { + // generic handler for files in a bundle +public: + virtual void populateMustPreserveSymbols(BitcodeObfuscator* _obfuscator) { } + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path) { }; + xar_file_t getXARFile() { return _xar_file; } + + FileHandler(char* content, size_t size) : + _parent(NULL), _xar_file(NULL), _file_buffer(content), _file_size(size) { } // eager construct + FileHandler(xar_t parent, xar_file_t xar_file) : + _parent(parent), _xar_file(xar_file), _file_buffer(NULL), _file_size(0) { } // lazy construct + virtual ~FileHandler() { } + +protected: + void initFile() { + if (!_file_buffer) { + if (xar_extract_tobuffersz(_parent, _xar_file, &_file_buffer, &_file_size) != 0) + throwf("could not extract files from bitcode bundle"); + } + } + void destroyFile() { + if (_parent) + free(_file_buffer); + } + + xar_t _parent; + xar_file_t _xar_file; + char* _file_buffer; + size_t _file_size; +}; + +class BundleHandler : public FileHandler { +public: + BundleHandler(char* bundleContent, size_t bundleSize, const Options& options) : + FileHandler(bundleContent, bundleSize), _xar(NULL), _temp_dir(NULL), _options(options) { } + BundleHandler(xar_t parent, xar_file_t xar_file, const Options& options) : + FileHandler(parent, xar_file), _xar(NULL), _temp_dir(NULL), _options(options) { } + + ~BundleHandler(); + + virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; + +private: + void init(); + void copyXARProp(xar_file_t src, xar_file_t dst); + + xar_t _xar; + char* _temp_dir; + const Options& _options; + std::vector _handlers; +}; + +class BitcodeHandler : public FileHandler { +public: + BitcodeHandler(char* content, size_t size) : FileHandler(content, size) { } + BitcodeHandler(xar_t parent, xar_file_t xar_file) : FileHandler(parent, xar_file) { } + + ~BitcodeHandler(); + + virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override { } // Don't need to preserve symbols + virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; +}; + +class ObjectHandler : public FileHandler { +public: + ObjectHandler(char* content, size_t size) : + FileHandler(content, size) { } + ObjectHandler(xar_t parent, xar_file_t xar_file) : + FileHandler(parent, xar_file) { } + + ~ObjectHandler(); + + void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; + void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; + +}; + + +class BitcodeBundle { +public: + BitcodeBundle(const Options& opts, ld::Internal& internal) : + _options(opts), _state(internal) { } + ~BitcodeBundle() { } + void doPass(); + +private: + const Options& _options; + ld::Internal& _state; +}; + +BitcodeAtom::BitcodeAtom() +: ld::Atom(bitcodeBundleSection, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _size(1) +{ + // initialize a marker of 1 byte + _content = (uint8_t*)calloc(1,1); +} + +BitcodeAtom::BitcodeAtom(BitcodeTempFile& tempfile) + : ld::Atom(bitcodeBundleSection, + ld::Atom::definitionRegular, ld::Atom::combineNever, + ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, + ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), + _content(tempfile._content), _size(tempfile._size) +{ + // Creating the Atom will transfer the ownership of the buffer from Tempfile to Atom + tempfile._content = NULL; +} + +BitcodeTempFile::BitcodeTempFile(const char* path, bool deleteAfterRead = true) + : _path(path), _deleteAfterRead(deleteAfterRead) +{ + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("could not open bitcode temp file: %s", path); + struct stat stat_buf; + ::fstat(fd, &stat_buf); + _content = (uint8_t*)malloc(stat_buf.st_size); + if ( _content == NULL ) + throwf("could not process bitcode temp file: %s", path); + if ( read(fd, _content, stat_buf.st_size) != stat_buf.st_size ) + throwf("could not read bitcode temp file: %s", path); + ::close(fd); + _size = stat_buf.st_size; +} + +BitcodeTempFile::~BitcodeTempFile() +{ + free(_content); + if ( _deleteAfterRead ) { + if ( ::unlink(_path) != 0 ) + throwf("could not remove temp file: %s", _path); + } +} + +BitcodeObfuscator::BitcodeObfuscator() +{ + // check if apple internal libLTO is used + if ( ::lto_get_version() == NULL ) + throwf("libLTO is not loaded"); + _lto_hide_symbols = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_hide_symbols"); + _lto_write_reverse_map = (lto_codegen_output_t) dlsym(RTLD_DEFAULT, "lto_codegen_write_symbol_reverse_map"); + _lto_reset_context = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_reset_context"); + if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL || + _lto_reset_context == NULL || ::lto_api_version() < 14 ) + throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); + _obfuscator = ::lto_codegen_create_in_local_context(); +#if LTO_API_VERSION >= 14 + lto_codegen_set_should_internalize(_obfuscator, false); +#endif +} + +BitcodeObfuscator::~BitcodeObfuscator() +{ + ::lto_codegen_dispose(_obfuscator); +} + +void BitcodeObfuscator::addMustPreserveSymbols(const char* name) +{ + ::lto_codegen_add_must_preserve_symbol(_obfuscator, name); +} + +void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath) +{ +#if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL + lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator); + if ( module == NULL ) + throwf("object contains invalid bitcode: %s", filePath); + ::lto_codegen_set_module(_obfuscator, module); + (*_lto_hide_symbols)(_obfuscator); +#if LTO_API_VERSION >= 15 + ::lto_codegen_set_should_embed_uselists(_obfuscator, true); +#endif + ::lto_codegen_write_merged_modules(_obfuscator, outputPath); + (*_lto_reset_context)(_obfuscator); +#endif + return; +} + +void BitcodeObfuscator::writeSymbolMap(const char *outputPath) +{ + (*_lto_write_reverse_map)(_obfuscator, outputPath); +} + +BundleHandler::~BundleHandler() +{ + // free buffers + destroyFile(); + // free handlers + for (auto handler : _handlers) + delete handler; + + // delete temp file if not -save-temps + if ( _xar ) { + xar_close(_xar); + std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); + if ( !_options.saveTempFiles() && ::unlink(oldXARPath.c_str()) != 0) + warning("could not delete temp file: %s", oldXARPath.c_str()); + } + + if ( _temp_dir ) { + if ( !_options.saveTempFiles() && ::rmdir(_temp_dir) != 0 ) + warning("could not delete temp directory: %s", _temp_dir); + free(_temp_dir); + } +} + +BitcodeHandler::~BitcodeHandler() +{ + destroyFile(); +} + +ObjectHandler::~ObjectHandler() +{ + destroyFile(); +} + +void BundleHandler::init() +{ + if ( _xar != NULL ) + return; + + // make temp directory + const char* finalOutput = _options.outputFilePath(); + _temp_dir = (char*)malloc(PATH_MAX * sizeof(char)); + // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX + // If so, fall back to /tmp + if ( strlen(finalOutput) + 30 >= PATH_MAX ) + sprintf(_temp_dir, "/tmp/ld.bundle.XXXXXX"); + else + sprintf(_temp_dir, "%s.bundle.XXXXXX", finalOutput); + ::mkdtemp(_temp_dir); + + // write the bundle to the temp_directory + initFile(); + std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); + int f = ::open(oldXARPath.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if ( f == -1 ) + throwf("could not write file to temp directory: %s", _temp_dir); + if ( ::write(f, _file_buffer, _file_size) != (int)_file_size ) + throwf("failed to write content to temp file: %s", oldXARPath.c_str()); + ::close(f); + + // read the xar file + _xar = xar_open(oldXARPath.c_str(), READ); + + // Init the vector of handler + xar_iter_t iter = xar_iter_new(); + if ( !iter ) + throwf("could not aquire iterator for the bitcode bundle"); + for ( xar_file_t f = xar_file_first(_xar, iter); f; f = xar_file_next(iter) ) { + const char* filetype = NULL; + if ( xar_prop_get(f, "file-type", &filetype) != 0 ) + throwf("could not get the file type for the bitcode bundle"); + if ( strcmp(filetype, "Bundle") == 0 ) + _handlers.push_back(new BundleHandler(_xar, f, _options)); + else if ( strcmp(filetype, "Object") == 0 ) + _handlers.push_back(new ObjectHandler(_xar, f)); + else if ( strcmp(filetype, "Bitcode") == 0 || strcmp(filetype, "LTO") == 0 ) + _handlers.push_back(new BitcodeHandler(_xar, f)); + else + assert(0 && "Unknown file type"); + } + xar_iter_free(iter); +} + +void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst) +{ + // copy the property in the XAR. + // Since XAR API can only get the first value from the key, + // Deleting the value after read. + int i = 0; + while (1) { + xar_iter_t p = xar_iter_new(); + const char* key = xar_prop_first(src, p); + for (int x = 0; x < i; x++) + key = xar_prop_next(p); + if ( !key ) + break; + const char* val = NULL; + xar_prop_get(src, key, &val); + if ( // Info from bitcode files + strcmp(key, "file-type") == 0 || + strcmp(key, "clang/cmd") == 0 || + strcmp(key, "swift/cmd") == 0 || + // Info from linker subdoc + strcmp(key, "version") == 0 || + strcmp(key, "architecture") == 0 || + strcmp(key, "hide-symbols") == 0 || + strcmp(key, "platform") == 0 || + strcmp(key, "sdkversion") == 0 || + strcmp(key, "dylibs/lib") == 0 || + strcmp(key, "link-options/option") == 0 ) { + xar_prop_create(dst, key, val); + xar_prop_unset(src, key); + } else + ++ i; + xar_iter_free(p); + } +} + +void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) +{ + // init the handler + if ( _xar == NULL ) + init(); + + // iterate through the XAR file and add symbols + for ( auto handler : _handlers ) + handler->populateMustPreserveSymbols(obfuscator); +} + +void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) +{ + initFile(); + // Parse the object file and add the symbols + std::vector symbols; + if ( mach_o::relocatable::getNonLocalSymbols((uint8_t*)_file_buffer, symbols) ) { + for ( auto sym : symbols ) + obfuscator->addMustPreserveSymbols(sym); + } +} + +void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) +{ + // init the handler + if ( _xar == NULL ) + init(); + + // creating the new xar + xar_t x = xar_open(path, WRITE); + if (x == NULL) + throwf("could not open output bundle to write %s", path); + // Disable compression + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) + throwf("could not disable compression for bitcode bundle"); + + // iterate through the XAR file and obfuscate + for ( auto handler : _handlers ) { + const char* name = NULL; + xar_file_t f = handler->getXARFile(); + if ( xar_prop_get(f, "name", &name) != 0 ) + throwf("could not get the name of the file from bitcode bundle"); + char outputPath[PATH_MAX]; + sprintf(outputPath, "%s/%s", _temp_dir, name); + handler->obfuscateAndWriteToPath(obfuscator, outputPath); + BitcodeTempFile* bcOut = new BitcodeTempFile(outputPath, !_options.saveTempFiles()); + xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize()); + copyXARProp(f, bcEntry); + delete bcOut; + } + + // copy the subdoc as well + for ( xar_subdoc_t sub = xar_subdoc_first(_xar); sub; sub = xar_subdoc_next(sub) ) { + const char *name = xar_subdoc_name(sub); + xar_subdoc_t newDoc = xar_subdoc_new(x, name); + copyXARProp((xar_file_t) sub, (xar_file_t) newDoc); + } + xar_close(x); +} + +void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) +{ + initFile(); + ld::Bitcode bc((uint8_t*)_file_buffer, _file_size); + obfuscator->bitcodeHideSymbols(&bc, path, path); +} + +void ObjectHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) +{ + initFile(); + int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if ( f == -1 || ::write(f, _file_buffer, _file_size) != (int)_file_size ) + throwf("failed to write content to temp file: %s", path); + ::close(f); +} + +void BitcodeBundle::doPass() +{ + if ( _state.embedMarkerOnly ) { + assert( _options.outputKind() != Options::kDynamicExecutable && + _options.outputKind() != Options::kStaticExecutable && + "Don't emit marker for executables"); + BitcodeAtom* marker = new BitcodeAtom(); + _state.addAtom(*marker); + return; + } + + if ( _state.filesWithBitcode.empty() && _state.ltoBitcodePath.empty() ) + return; + // Create tempdir, the temp directory should be OUTPUT/main.exe.bundle-XXXXXX + char tempdir[PATH_MAX]; + const char* finalOutput = _options.outputFilePath(); + // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX + // If so, fall back to /tmp + if ( strlen(finalOutput) + 30 >= PATH_MAX ) + sprintf(tempdir, "/tmp/ld.bundle.XXXXXX"); + else + sprintf(tempdir, "%s.bundle.XXXXXX", finalOutput); + ::mkdtemp(tempdir); + // A lookup map to look for BundlerHandler base on filename + std::unordered_map handlerMap; + + BitcodeObfuscator* obfuscator = _options.hideSymbols() ? new BitcodeObfuscator() : NULL; + // Build must keep symbols if we need to hide all the symbols + if ( _options.hideSymbols() ) { + // Go through all the atoms and decide if it should be obfuscated. + // The following symbols are kept: + // 1. entry point + // 2. undefined symbols + // 3. symbols must not be stripped + // 4. all the globals if the globals are dead_strip root (ex. dylibs) + // 5. there is an exported symbol list suggests the symbol should be exported + // 6. the special symbols supplied by linker + for ( auto § : _state.sections ) { + for ( auto &atom : sect->atoms ) { + if ( atom == _state.entryPoint || + atom->definition() == ld::Atom::definitionProxy || + atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip || + ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) || + ( _options.hasExportRestrictList() && _options.shouldExport(atom->name())) ) + obfuscator->addMustPreserveSymbols(atom->name()); + } + } + // If there are assembly sources, add globals and undefined symbols from them as well + for ( auto &f : _state.filesWithBitcode ) { + if ( ld::AsmBitcode* ab = dynamic_cast(f->getBitcode()) ) { + ObjectHandler objHandler((char*)ab->getContent(), ab->getSize()); + objHandler.populateMustPreserveSymbols(obfuscator); + } else if ( ld::BundleBitcode* bb = dynamic_cast(f->getBitcode()) ) { + BundleHandler* bh = new BundleHandler((char*)bb->getContent(), bb->getSize(), _options); + bh->populateMustPreserveSymbols(obfuscator); + handlerMap.emplace(std::string(f->path()), bh); + } + } + // special symbols supplied by linker + obfuscator->addMustPreserveSymbols("___dso_handle"); + obfuscator->addMustPreserveSymbols("__mh_execute_header"); + obfuscator->addMustPreserveSymbols("__mh_dylib_header"); + obfuscator->addMustPreserveSymbols("__mh_bundle_header"); + obfuscator->addMustPreserveSymbols("__mh_dylinker_header"); + obfuscator->addMustPreserveSymbols("__mh_object_header"); + obfuscator->addMustPreserveSymbols("__mh_preload_header"); + } + + // Open XAR output + xar_t x; + char outFile[PATH_MAX]; + sprintf(outFile, "%s/bundle.xar", tempdir); + + // By default, it uses gzip to compress and SHA1 as checksum + x = xar_open(outFile, WRITE); + if (x == NULL) + throwf("could not open output bundle to write %s", outFile); + // Disable compression + if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) + throwf("could not disable compression for bitcode bundle"); + + // Sort all the object file according to oridnal order + std::sort(_state.filesWithBitcode.begin(), _state.filesWithBitcode.end(), + [](const ld::relocatable::File* a, const ld::relocatable::File* b) { + return a->ordinal() < b->ordinal(); + }); + + // Copy each bitcode file into archive + int index = 1; + char formatString[10]; + sprintf(formatString, "%%0%ud", (unsigned int)log10(_state.filesWithBitcode.size()) + 1); + for ( auto &obj : _state.filesWithBitcode ) { + assert(obj->getBitcode() != NULL && "File should contain bitcode"); + char outFilePath[16]; + sprintf(outFilePath, formatString, index++); + if ( ld::LLVMBitcode* llvmbc = dynamic_cast(obj->getBitcode()) ) { + // Handle clang and swift bitcode + xar_file_t bcFile = NULL; + if ( _options.hideSymbols() && !llvmbc->isMarker() ) { // dont strip if it is just a marker + char tempfile[PATH_MAX]; + sprintf(tempfile, "%s/%s.bc", tempdir, outFilePath); + obfuscator->bitcodeHideSymbols(llvmbc, obj->path(), tempfile); + BitcodeTempFile* bcTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); + bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bcTemp->getContent(), bcTemp->getSize()); + delete bcTemp; + } else { + bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)const_cast(llvmbc->getContent()), llvmbc->getSize()); + } + if ( bcFile == NULL ) + throwf("could not add bitcode from %s to bitcode bundle", obj->path()); + if ( xar_prop_set(bcFile, "file-type", "Bitcode") != 0 ) + throwf("could not set bitcode property for %s in bitcode bundle", obj->path()); + // Write commandline options + std::string tagName = std::string(llvmbc->getBitcodeName()) + std::string("/cmd"); + for ( uint32_t i = 0; i < llvmbc->getCmdSize(); ++i ) { + if ( i == 0 || llvmbc->getCmdline()[i-1] == '\0' ) { + if ( xar_prop_create(bcFile, tagName.c_str(), (const char *)llvmbc->getCmdline() + i) ) + throwf("could not set cmdline to XAR file"); + } + } + } + else if ( ld::BundleBitcode* bundlebc = dynamic_cast(obj->getBitcode()) ) { + xar_file_t bundleFile = NULL; + if ( _options.hideSymbols() && !bundlebc->isMarker() ) { // dont strip if it is just a marker + char tempfile[PATH_MAX]; + sprintf(tempfile, "%s/%s.xar", tempdir, outFilePath); + auto search = handlerMap.find(std::string(obj->path())); + assert( search != handlerMap.end() && "Cannot find handler"); + search->second->obfuscateAndWriteToPath(obfuscator, tempfile); + BitcodeTempFile* bundleTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); + bundleFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bundleTemp->getContent(), bundleTemp->getSize()); + delete bundleTemp; + } else { + bundleFile = xar_add_frombuffer(x, NULL, outFilePath, + (char*)const_cast(bundlebc->getContent()), + bundlebc->getSize()); + } + if ( bundleFile == NULL ) + throwf("could not add bitcode from the bundle %s to bitcode bundle", obj->path()); + if ( xar_prop_set(bundleFile, "file-type", "Bundle") != 0 ) + throwf("could not set bundle property for %s in bitcode bundle", obj->path()); + } + else if ( ld::AsmBitcode* asmbc = dynamic_cast(obj->getBitcode()) ) { + xar_file_t objFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)asmbc->getContent(), asmbc->getSize()); + if ( objFile == NULL ) + throwf("could not add obj file %s to bitcode bundle", obj->path()); + if ( xar_prop_set(objFile, "file-type", "Object") != 0 ) + throwf("could not set object property for %s in bitcode bundle", obj->path()); + } + else { + assert(false && "Unknown bitcode"); + } + } + + // Write merged LTO bitcode + if ( !_state.ltoBitcodePath.empty() ) { + xar_file_t ltoFile = NULL; + BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles()); + if ( _options.hideSymbols() ) { + ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize()); + char ltoTempFile[PATH_MAX]; + sprintf(ltoTempFile, "%s/lto.bc", tempdir); + obfuscator->bitcodeHideSymbols(<oBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile); + BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles()); + ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize()); + delete ltoStrip; + } else { + ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize()); + } + if ( ltoFile == NULL ) + throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str()); + if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 ) + throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str()); + delete ltoTemp; + } + + // Common LinkOptions + std::vector linkCmd = _options.writeBitcodeLinkOptions(); + + // support -sectcreate option + for ( auto extraSect = _options.extraSectionsBegin(); extraSect != _options.extraSectionsEnd(); ++ extraSect ) { + std::string sectName = std::string(extraSect->segmentName) + std::string(",") + std::string(extraSect->sectionName); + BitcodeTempFile* sectFile = new BitcodeTempFile(extraSect->path, false); + xar_file_t sectXar = xar_add_frombuffer(x, NULL, sectName.c_str(), (char*)sectFile->getContent(), sectFile->getSize()); + if ( sectXar == NULL ) + throwf("could not encode sectcreate file %s into bitcode bundle", extraSect->path); + if ( xar_prop_set(sectXar, "file-type", "Section") != 0 ) + throwf("could not set bitcode property for %s", sectName.c_str()); + delete sectFile; + linkCmd.push_back("-sectcreate"); + linkCmd.push_back(extraSect->segmentName); + linkCmd.push_back(extraSect->sectionName); + linkCmd.push_back(sectName); + } + + // Write exports file + if ( _options.hasExportMaskList() ) { + linkCmd.push_back("-exported_symbols_list"); + linkCmd.push_back("exports.exp"); + const char* exportsPath = "exports.exp"; + std::vector exports = _options.exportsData(); + std::string exps; + for (std::vector::iterator it = exports.begin(); + it != exports.end(); ++ it) { + exps += *it; + exps += "\n"; + } + // always append an empty line so exps cannot be empty. rdar://problem/22404253 + exps += "\n"; + xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast(exps.data()), exps.size()); + if (exportsFile == NULL) + throwf("could not add exports list to bitcode bundle"); + if (xar_prop_set(exportsFile, "file-type", "Exports") != 0) + throwf("could not set exports property in bitcode bundle"); + } + + // Create subdoc to write link information + xar_subdoc_t linkXML = xar_subdoc_new(x, "Ld"); + if ( linkXML == NULL ) + throwf("could not create XML in bitcode bundle"); + + // Write version number + if ( xar_prop_create((xar_file_t)linkXML, "version", BITCODE_XAR_VERSION) != 0 ) + throwf("could not add version number to bitcode bundle"); + + // Arch + if ( xar_prop_create((xar_file_t)linkXML, "architecture", _options.architectureName()) != 0 ) + throwf("could not add achitecture name to bitcode bundle"); + + // Opt-out symbols + if ( _options.hideSymbols() ) { + if ( xar_prop_create((xar_file_t)linkXML, "hide-symbols", "1") != 0 ) + throwf("could not add property to bitcode bundle"); + } + + // Write SDK version + if ( _options.sdkPaths().size() > 1 ) + throwf("only one -syslibroot is accepted for bitcode bundle"); + if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.getPlatformStr().c_str()) != 0 ) + throwf("could not add platform name to bitcode bundle"); + if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 ) + throwf("could not add SDK version to bitcode bundle"); + + // Write dylibs + const char* sdkRoot = NULL; + if ( !_options.sdkPaths().empty() ) + sdkRoot = _options.sdkPaths().front(); + if ( !_state.dylibs.empty() ) { + std::vector SDKPaths = _options.sdkPaths(); + char dylibPath[PATH_MAX]; + for ( auto &dylib : _state.dylibs ) { + // For every dylib/framework, figure out if it is coming from a SDK + // if it is coming from some SDK, we parse the path to figure out which SDK + // If -syslibroot is pointing to a SDK, it should end with PlatformX.Y.sdk/ + if (sdkRoot && strncmp(dylib->path(), sdkRoot, strlen(sdkRoot)) == 0) { + // dylib/framework from one of the -syslibroot + // The path start with a string template + strcpy(dylibPath, "{SDKPATH}/"); + // append the path of dylib/frameowrk in the SDK + strcat(dylibPath, dylib->path() + strlen(sdkRoot)); + } else { + // Not in any SDKs, then assume it is a user dylib/framework + // strip off all the path in the front + const char* dylib_name = strrchr(dylib->path(), '/'); + dylib_name = (dylib_name == NULL) ? dylib->path() : dylib_name + 1; + strcpy(dylibPath, dylib_name); + } + if ( dylib->forcedWeakLinked() ) { + if ( xar_prop_create((xar_file_t)linkXML, "dylibs/weak", dylibPath) != 0) + throwf("could not add dylib options to bitcode bundle"); + } else { + if ( xar_prop_create((xar_file_t)linkXML, "dylibs/lib", dylibPath) != 0) + throwf("could not add dylib options to bitcode bundle"); + } + } + } + + // Write link-line into archive + for ( auto &it : linkCmd ) { + if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0) + throwf("could not add link options to bitcode bundle"); + } + // Finish writing + xar_close(x); + + // Read the file back + BitcodeTempFile* xarTemp = new BitcodeTempFile(outFile, !_options.saveTempFiles()); + + // Create an Atom and add to the list + BitcodeAtom* bundleAtom = new BitcodeAtom(*xarTemp); + _state.addAtom(*bundleAtom); + + // write the reverse mapping file if required + if ( _options.hideSymbols() && !_options.reverseMapTempPath().empty() ) + obfuscator->writeSymbolMap(_options.reverseMapTempPath().c_str()); + + // Clean up local variables + delete xarTemp; + delete obfuscator; + for ( auto &entry: handlerMap ) + delete entry.second; + // delete temp directory if not using -save-temps + // only do so after all the BitcodeTempFiles are deleted. + if ( !_options.saveTempFiles() ) { + if ( ::rmdir(tempdir) != 0 ) + warning("temp directory cannot be removed: %s", tempdir); + } +} + + + +// called by linker to write bitcode bundle into a mach-o section +void doPass(const Options& opts, ld::Internal& internal) { + BitcodeBundle BB(opts, internal); + BB.doPass(); +} + + +} // namespace bitcode_bundle +} // namespace passes +} // namespace ld diff --git a/src/ld/passes/bitcode_bundle.h b/src/ld/passes/bitcode_bundle.h new file mode 100644 index 0000000..c87a59e --- /dev/null +++ b/src/ld/passes/bitcode_bundle.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _BITCODE_BUNDLE_H_ +#define _BITCODE_BUNDLE_H_ + +#include "Options.h" +#include "ld.hpp" + +namespace ld { +namespace passes { +namespace bitcode_bundle { + +// called by linker to write bitcode bundle into a mach-o section +extern void doPass(const Options& opts, ld::Internal& internal); + + +} // namespace bitcode_bundle +} // namespace passes +} // namespace ld + +#endif /* defined(_BITCODE_BUNDLE_H_) */ diff --git a/src/ld/passes/branch_island.cpp b/src/ld/passes/branch_island.cpp index a8fb325..f7465d0 100644 --- a/src/ld/passes/branch_island.cpp +++ b/src/ld/passes/branch_island.cpp @@ -552,7 +552,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: if ( target->section().type() == ld::Section::typeStub ) dstAddr = totalTextSize; int64_t displacement = dstAddr - srcAddr; - TargetAndOffset finalTargetAndOffset = { target, addend }; + TargetAndOffset finalTargetAndOffset = { target, (uint32_t)addend }; const int64_t kBranchLimit = kBetweenRegions; if ( crossSectionBranch && ((displacement > kBranchLimit) || (displacement < (-kBranchLimit))) ) { const ld::Atom* island; @@ -565,6 +565,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: island, island->name(), displacement); ++islandCount; regionsIslands[0]->push_back(island); + state.atomToSection[island] = textSection; } else { island = pos->second; @@ -588,6 +589,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: (*region)[finalTargetAndOffset] = island; if (_s_log) fprintf(stderr, "added forward branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); + state.atomToSection[island] = textSection; ++islandCount; nextTarget = island; } @@ -614,6 +616,7 @@ static void makeIslandsForSection(const Options& opts, ld::Internal& state, ld:: (*region)[finalTargetAndOffset] = island; if (_s_log) fprintf(stderr, "added back branching island %p %s to region %d for %s\n", island, island->name(), i, atom->name()); regionsIslands[i]->push_back(island); + state.atomToSection[island] = textSection; ++islandCount; prevTarget = island; } diff --git a/src/ld/passes/branch_shim.cpp b/src/ld/passes/branch_shim.cpp index 840a391..efc010c 100644 --- a/src/ld/passes/branch_shim.cpp +++ b/src/ld/passes/branch_shim.cpp @@ -328,6 +328,7 @@ void doPass(const Options& opts, ld::Internal& state) } shims.push_back(shim); thumbToAtomMap[target] = shim; + state.atomToSection[shim] = sect; } else { shim = pos->second; @@ -361,6 +362,7 @@ void doPass(const Options& opts, ld::Internal& state) shim = new ARMtoThumbShimAtom(target, *sect); shims.push_back(shim); atomToThumbMap[target] = shim; + state.atomToSection[shim] = sect; } else { shim = pos->second; diff --git a/src/ld/passes/dylibs.cpp b/src/ld/passes/dylibs.cpp index af1ecf6..f77f5cd 100644 --- a/src/ld/passes/dylibs.cpp +++ b/src/ld/passes/dylibs.cpp @@ -30,6 +30,7 @@ #include #include +#include #include "ld.hpp" #include "dylibs.h" diff --git a/src/ld/passes/got.cpp b/src/ld/passes/got.cpp index 66caf34..01c2e30 100644 --- a/src/ld/passes/got.cpp +++ b/src/ld/passes/got.cpp @@ -44,8 +44,8 @@ class File; // forward reference class GOTEntryAtom : public ld::Atom { public: - GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool is64) - : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + GOTEntryAtom(ld::Internal& internal, const ld::Atom* target, bool weakImport, bool weakDef, bool is64) + : ld::Atom(weakDef ? _s_sectionWeak : _s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeNonLazyPointer, symbolTableNotIn, false, false, false, (is64 ? ld::Atom::Alignment(3) : ld::Atom::Alignment(2))), _fixup(0, ld::Fixup::k1of1, (is64 ? ld::Fixup::kindStoreTargetAddressLittleEndian64 : ld::Fixup::kindStoreTargetAddressLittleEndian32), target), @@ -68,13 +68,16 @@ private: bool _is64; static ld::Section _s_section; + static ld::Section _s_sectionWeak; }; ld::Section GOTEntryAtom::_s_section("__DATA", "__got", ld::Section::typeNonLazyPointer); +ld::Section GOTEntryAtom::_s_sectionWeak("__DATA", "__got_weak", ld::Section::typeNonLazyPointer); -static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable) +static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom* targetOfGOT, const ld::Fixup* fixup, bool* optimizable, bool* targetIsExternalWeakDef) { + *targetIsExternalWeakDef = false; switch (fixup->kind) { case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad: #if SUPPORT_ARCH_arm64 @@ -92,14 +95,15 @@ static bool gotFixup(const Options& opts, ld::Internal& internal, const ld::Atom || (targetOfGOT->section().type() == ld::Section::typeTentativeDefs)) ) { *optimizable = false; } - if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { + if ( targetOfGOT->scope() == ld::Atom::scopeGlobal ) { // cannot do LEA optimization if target is weak exported symbol - if ( (targetOfGOT->definition() == ld::Atom::definitionRegular) && (targetOfGOT->combine() == ld::Atom::combineByName) ) { + if ( ((targetOfGOT->definition() == ld::Atom::definitionRegular) || (targetOfGOT->definition() == ld::Atom::definitionProxy)) && (targetOfGOT->combine() == ld::Atom::combineByName) ) { switch ( opts.outputKind() ) { case Options::kDynamicExecutable: case Options::kDynamicLibrary: case Options::kDynamicBundle: case Options::kKextBundle: + *targetIsExternalWeakDef = true; *optimizable = false; break; case Options::kStaticExecutable: @@ -199,6 +203,7 @@ void doPass(const Options& opts, ld::Internal& internal) // don't create GOT atoms during this loop because that could invalidate the sections iterator std::vector atomsReferencingGOT; std::map weakImportMap; + std::map weakDefMap; atomsReferencingGOT.reserve(128); for (std::vector::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; @@ -223,7 +228,8 @@ void doPass(const Options& opts, ld::Internal& internal) break; } bool optimizable; - if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + bool targetIsExternalWeakDef; + if ( !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) ) continue; if ( optimizable ) { // change from load of GOT entry to lea of target @@ -264,6 +270,8 @@ void doPass(const Options& opts, ld::Internal& internal) } if ( gotMap.count(targetOfGOT) == 0 ) gotMap[targetOfGOT] = NULL; + // record if target is weak def + weakDefMap[targetOfGOT] = targetIsExternalWeakDef; // record weak_import attribute std::map::iterator pos = weakImportMap.find(targetOfGOT); if ( pos == weakImportMap.end() ) { @@ -319,7 +327,7 @@ void doPass(const Options& opts, ld::Internal& internal) // make GOT entries for (auto& entry : gotMap) { if ( entry.second == NULL ) { - entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], is64); + entry.second = new GOTEntryAtom(internal, entry.first, weakImportMap[entry.first], opts.useDataConstSegment() && weakDefMap[entry.first], is64); if (log) fprintf(stderr, "making new GOT slot for %s, gotMap[%p] = %p\n", entry.first->name(), entry.first, entry.second); } } @@ -348,7 +356,8 @@ void doPass(const Options& opts, ld::Internal& internal) break; } bool optimizable; - if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable) ) + bool targetIsExternalWeakDef; + if ( (targetOfGOT == NULL) || !gotFixup(opts, internal, targetOfGOT, fit, &optimizable, &targetIsExternalWeakDef) ) continue; if ( !optimizable ) { // GOT use not optimized away, update to bind to GOT entry diff --git a/src/ld/passes/huge.cpp b/src/ld/passes/huge.cpp index 51dda80..693976c 100644 --- a/src/ld/passes/huge.cpp +++ b/src/ld/passes/huge.cpp @@ -30,6 +30,7 @@ #include #include +#include #include "ld.hpp" #include "huge.h" @@ -94,6 +95,7 @@ void doPass(const Options& opts, ld::Internal& state) const ld::Atom* atom = *ait; if ( atom->size() > 1024*1024 ) { hugeSection->atoms.push_back(atom); + state.atomToSection[atom] = hugeSection; if (log) fprintf(stderr, "moved to __huge: %s, size=%llu\n", atom->name(), atom->size()); *ait = NULL; // change atom to NULL for later bulk removal movedSome = true; diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index 2bbf54a..ad4673c 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -805,7 +805,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) std::set nlcatListAtoms; for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) { + if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strncmp(sect->segmentName(), "__DATA", 6) == 0) ) { for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* categoryListElementAtom = *ait; for (unsigned int offset=0; offset < categoryListElementAtom->size(); offset += sizeof(pint_t)) { @@ -865,7 +865,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) } } // record method list section - if ( (strcmp(sect->sectionName(), "__objc_const") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) + if ( (strcmp(sect->sectionName(), "__objc_const") == 0) && (strncmp(sect->segmentName(), "__DATA", 6) == 0) ) methodListSection = sect; } @@ -885,9 +885,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newClassRO = Class::setInstanceMethodList(state, classAtom, newInstanceMethodListAtom, deadAtoms); // add new method list to final sections methodListSection->atoms.push_back(newInstanceMethodListAtom); + state.atomToSection[newInstanceMethodListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } } // if any category adds class methods, generate new merged method list, and replace @@ -897,9 +899,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newClassRO = Class::setClassMethodList(state, classAtom, newClassMethodListAtom, deadAtoms); // add new method list to final sections methodListSection->atoms.push_back(newClassMethodListAtom); + state.atomToSection[newClassMethodListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } } // if any category adds protocols, generate new merged protocol list, and replace @@ -910,13 +914,16 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newMetaClassRO = Class::setClassProtocolList(state, classAtom, newProtocolListAtom, deadAtoms); // add new protocol list to final sections methodListSection->atoms.push_back(newProtocolListAtom); + state.atomToSection[newProtocolListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } if ( newMetaClassRO != NULL ) { assert(strcmp(newMetaClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newMetaClassRO); + state.atomToSection[newMetaClassRO] = methodListSection; } } // if any category adds properties, generate new merged property list, and replace @@ -926,9 +933,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) const ld::Atom* newClassRO = Class::setInstancePropertyList(state, classAtom, newPropertyListAtom, deadAtoms); // add new property list to final sections methodListSection->atoms.push_back(newPropertyListAtom); + state.atomToSection[newPropertyListAtom] = methodListSection; if ( newClassRO != NULL ) { assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0); methodListSection->atoms.push_back(newClassRO); + state.atomToSection[newClassRO] = methodListSection; } } diff --git a/src/ld/passes/order.cpp b/src/ld/passes/order.cpp index 139e761..a7d31f3 100644 --- a/src/ld/passes/order.cpp +++ b/src/ld/passes/order.cpp @@ -510,8 +510,14 @@ void Layout::buildOrdinalOverrideMap() switch ( atom->section().type() ) { case ld::Section::typeZeroFill: case ld::Section::typeTentativeDefs: - if ( atom->size() <= 512 ) - moveToData.insert(atom); + if ( atom->size() <= 512 ) { + const char* dstSeg; + bool wildCardMatch; + const ld::File* f = atom->file(); + const char* path = (f != NULL) ? f->path() : NULL; + if ( !_options.moveRwSymbol(atom->name(), path, dstSeg, wildCardMatch) ) + moveToData.insert(atom); + } break; default: break; @@ -579,6 +585,10 @@ void Layout::buildOrdinalOverrideMap() break; } } + // update atom-to-section map + for (std::set::iterator it=moveToData.begin(); it != moveToData.end(); ++it) { + _state.atomToSection[*it] = dataSect; + } } } diff --git a/src/ld/passes/stubs/stub_arm.hpp b/src/ld/passes/stubs/stub_arm.hpp index 5c8fc42..02ae454 100644 --- a/src/ld/passes/stubs/stub_arm.hpp +++ b/src/ld/passes/stubs/stub_arm.hpp @@ -234,8 +234,9 @@ class LazyPointerAtom : public ld::Atom { public: LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, bool stubToGlobalWeakDef, bool stubToResolver, - bool weakImport, bool close) - : ld::Atom(close ? _s_sectionClose : _s_section, ld::Atom::definitionRegular, + bool weakImport, bool close, bool usingDataConst) + : ld::Atom(selectSection(close, stubToGlobalWeakDef, stubToResolver, usingDataConst), + ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeLazyPointer, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), @@ -243,7 +244,7 @@ public: _resolverHelper(pass, stubTo, this), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, stubToResolver ? &_resolverHelper : (stubToGlobalWeakDef ? &stubTo : &_helper)), - _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { + _fixup2(0, ld::Fixup::k1of1, ld::Fixup::kindLazyTarget, &stubTo) { _fixup2.weakImport = weakImport; pass.addAtom(*this); if ( stubToResolver ) pass.addAtom(_resolverHelper); @@ -261,6 +262,17 @@ public: virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: + static ld::Section& selectSection(bool close, bool stubToGlobalWeakDef, bool stubToResolver, bool usingDataConst) { + if ( close ) + return _s_sectionClose; + else if ( stubToGlobalWeakDef && usingDataConst ) + return _s_sectionWeak; + else if ( stubToResolver && usingDataConst ) + return _s_sectionResolver; + else + return _s_section; + } + const ld::Atom& _stubTo; StubHelperAtom _helper; ResolverHelperAtom _resolverHelper; @@ -269,10 +281,14 @@ private: static ld::Section _s_section; static ld::Section _s_sectionClose; + static ld::Section _s_sectionResolver; + static ld::Section _s_sectionWeak; }; ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); ld::Section LazyPointerAtom::_s_sectionClose("__DATA", "__lazy_symbol", ld::Section::typeLazyPointerClose); +ld::Section LazyPointerAtom::_s_sectionResolver("__DATA_DIRTY", "__la_resolver", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionWeak("__DATA", "__la_weak_ptr", ld::Section::typeLazyPointer); class NonLazyPointerAtom : public ld::Atom { @@ -364,12 +380,12 @@ ld::Section StubPICKextAtom::_s_section("__TEXT", "__stub", ld::Section::typeCod class StubPICAtom : public ld::Atom { public: StubPICAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool usingDataConst) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false, usingDataConst), _fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, &_lazyPointer), _fixup2(12, ld::Fixup::k2of4, ld::Fixup::kindSubtractTargetAddress, this), _fixup3(12, ld::Fixup::k3of4, ld::Fixup::kindSubtractAddend, 12), @@ -413,7 +429,7 @@ public: ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, false, false), _fixup(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, &_lazyPointer) { pass.addAtom(*this); } @@ -451,7 +467,7 @@ public: ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, true), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, true, false), _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARMLoad12, &_lazyPointer) { pass.addAtom(*this); } diff --git a/src/ld/passes/stubs/stub_arm64.hpp b/src/ld/passes/stubs/stub_arm64.hpp index 63382e5..09de1fa 100644 --- a/src/ld/passes/stubs/stub_arm64.hpp +++ b/src/ld/passes/stubs/stub_arm64.hpp @@ -241,8 +241,9 @@ ld::Section ResolverHelperAtom::_s_section("__TEXT", "__stub_helper", ld::S class LazyPointerAtom : public ld::Atom { public: LazyPointerAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) - : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool dataConstUsed) + : ld::Atom(selectSection(stubToGlobalWeakDef, stubToResolver, dataConstUsed), + ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, ld::Atom::typeLazyPointer, symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _stubTo(stubTo), @@ -269,6 +270,15 @@ public: virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup2)[1]; } private: + static ld::Section& selectSection(bool stubToGlobalWeakDef, bool stubToResolver, bool dataConstUsed) { + if ( stubToGlobalWeakDef && dataConstUsed ) + return _s_sectionWeak; + else if ( stubToResolver && dataConstUsed ) + return _s_sectionResolver; + else + return _s_section; + } + const ld::Atom& _stubTo; StubHelperAtom _helper; ResolverHelperAtom _resolverHelper; @@ -276,20 +286,24 @@ private: ld::Fixup _fixup2; static ld::Section _s_section; + static ld::Section _s_sectionResolver; + static ld::Section _s_sectionWeak; }; ld::Section LazyPointerAtom::_s_section("__DATA", "__la_symbol_ptr", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionResolver("__DATA_DIRTY", "__la_resolver", ld::Section::typeLazyPointer); +ld::Section LazyPointerAtom::_s_sectionWeak("__DATA", "__la_weak_ptr", ld::Section::typeLazyPointer); class StubAtom : public ld::Atom { public: StubAtom(ld::passes::stubs::Pass& pass, const ld::Atom& stubTo, - bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport) + bool stubToGlobalWeakDef, bool stubToResolver, bool weakImport, bool dataConstUsed) : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeStub, symbolTableNotIn, false, false, false, ld::Atom::Alignment(1)), _stubTo(stubTo), - _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport), + _lazyPointer(pass, stubTo, stubToGlobalWeakDef, stubToResolver, weakImport, dataConstUsed), _fixup1(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64Page21, &_lazyPointer), _fixup2(4, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressARM64PageOff12, &_lazyPointer), _fixup3(ld::Fixup::kindLinkerOptimizationHint, LOH_ARM64_ADRP_LDR, 0, 4) diff --git a/src/ld/passes/stubs/stubs.cpp b/src/ld/passes/stubs/stubs.cpp index c01f3f9..bee5f2f 100644 --- a/src/ld/passes/stubs/stubs.cpp +++ b/src/ld/passes/stubs/stubs.cpp @@ -164,16 +164,17 @@ const ld::Atom* Pass::stubableFixup(const ld::Fixup* fixup, ld::Internal& state) ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) { - //fprintf(stderr, "makeStub(target=%p %s in sect %s)\n", &target, target.name(), target.section().sectionName()); - bool stubToGlobalWeakDef = ( (target.scope() == ld::Atom::scopeGlobal) - && (target.definition() == ld::Atom::definitionRegular) - && (target.combine() == ld::Atom::combineByName) ); + //fprintf(stderr, "makeStub(target=%p %s in sect %s, def=%d)\n", &target, target.name(), target.section().sectionName(), target.definition()); + bool stubToGlobalWeakDef = ( (target.combine() == ld::Atom::combineByName) && + (((target.definition() == ld::Atom::definitionRegular) && (target.scope() == ld::Atom::scopeGlobal)) + || (target.definition() == ld::Atom::definitionProxy)) ); bool forLazyDylib = false; const ld::dylib::File* dylib = dynamic_cast(target.file()); if ( (dylib != NULL) && dylib->willBeLazyLoadedDylib() ) forLazyDylib = true; bool stubToResolver = (target.contentType() == ld::Atom::typeResolver); + bool usingDataConst = _options.useDataConstSegment(); if ( usingCompressedLINKEDIT() && !forLazyDylib ) { if ( _internal->compressedFastBinderProxy == NULL ) @@ -209,7 +210,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) if ( (_stubCount < 900) && !_mightBeInSharedRegion && !_largeText ) return new ld::passes::stubs::arm::StubCloseAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); else if ( _pic ) - return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + return new ld::passes::stubs::arm::StubPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst); else return new ld::passes::stubs::arm::StubNoPICAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); } @@ -226,7 +227,7 @@ ld::Atom* Pass::makeStub(const ld::Atom& target, bool weakImport) if ( (_options.outputKind() == Options::kKextBundle) && _options.kextsUseStubs() ) return new ld::passes::stubs::arm64::KextStubAtom(*this, target); else - return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport); + return new ld::passes::stubs::arm64::StubAtom(*this, target, stubToGlobalWeakDef, stubToResolver, weakImport, usingDataConst); break; #endif } diff --git a/src/ld/passes/tlvp.cpp b/src/ld/passes/tlvp.cpp index e84fc25..aec9562 100644 --- a/src/ld/passes/tlvp.cpp +++ b/src/ld/passes/tlvp.cpp @@ -48,7 +48,7 @@ public: symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian64, target), _target(target) - { _fixup.weakImport = weakImport; internal.addAtom(*this); } + { _fixup.weakImport = weakImport; internal.addAtom(*this); } virtual const ld::File* file() const { return NULL; } virtual const char* name() const { return _target->name(); } @@ -236,7 +236,7 @@ void doPass(const Options& opts, ld::Internal& internal) break; #if SUPPORT_ARCH_arm64 case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: - it->fixupWithTLVStore->kind = ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21; + it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPage21; break; case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: it->fixupWithTLVStore->kind = ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12; @@ -265,6 +265,9 @@ void doPass(const Options& opts, ld::Internal& internal) continue; for (std::vector::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { const ld::Atom* atom = *ait; + if ( ! opts.canUseThreadLocalVariables() ) { + throwf("targeted OS version does not support use of thread local variables in %s", atom->name()); + } for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { if ( fit->offsetInAtom != 0 ) { assert(fit->binding == ld::Fixup::bindingDirectlyBound && "thread variable def contains pointer to global"); diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index ceab63c..dfadf71 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -1244,11 +1244,13 @@ static ld::relocatable::File* createReader(const char* path) objOpts.verboseOptimizationHints = true; objOpts.armUsesZeroCostExceptions = true; objOpts.subType = sPreferredSubArch; + objOpts.srcKind = ld::relocatable::File::kSourceObj; #if 1 if ( ! foundFatSlice ) { cpu_type_t archOfObj; cpu_subtype_t subArchOfObj; - if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj) ) { + Options::Platform platform; + if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj, &platform) ) { objOpts.architecture = archOfObj; objOpts.subType = subArchOfObj; } diff --git a/src/other/dyldinfo.cpp b/src/other/dyldinfo.cpp index 6ac3311..f19538f 100644 --- a/src/other/dyldinfo.cpp +++ b/src/other/dyldinfo.cpp @@ -104,12 +104,19 @@ private: void printClassicLazyBindingInfo(); void printClassicBindingInfo(); void printSharedRegionInfo(); + const char* sharedRegionKindName(uint8_t kind); void printFunctionStartsInfo(); void printDylibsInfo(); void printDRInfo(); void printDataInCode(); void printFunctionStartLine(uint64_t addr); - const uint8_t* printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind); + const uint8_t* printSharedRegionV1InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end, uint8_t kind); + const uint8_t* printSharedRegionV2InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2InfoForEachULEB128AddressAndAdj(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2SectionPair(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2ToSectionOffset(const uint8_t* p, const uint8_t* end); + const uint8_t* printSharedRegionV2Kind(const uint8_t* p, const uint8_t* end); + pint_t relocBase(); const char* relocTypeName(uint8_t r_type); uint8_t segmentIndexForAddress(pint_t addr); @@ -129,6 +136,7 @@ private: const char* classicOrdinalName(int libraryOrdinal); pint_t* mappedAddressForVMAddress(pint_t vmaddress); const char* symbolNameForAddress(uint64_t); + const char* closestSymbolNameForAddress(uint64_t addr, uint64_t* offset, uint8_t sectIndex=0); const char* fPath; @@ -149,8 +157,10 @@ private: const macho_segment_command

* fFirstWritableSegment; bool fWriteableSegmentWithAddrOver4G; std::vector*>fSegments; + std::vector*> fSections; std::vector fDylibs; std::vector*> fDylibLoadCommands; + macho_section

fMachHeaderPseudoSection; }; @@ -286,7 +296,12 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen fPath = strdup(path); fHeader = (const macho_header

*)fileContent; - + + fMachHeaderPseudoSection.set_segname("__TEXT"); + fMachHeaderPseudoSection.set_sectname(""); + fMachHeaderPseudoSection.set_addr(0); + fSections.push_back(&fMachHeaderPseudoSection); + // get LC_DYLD_INFO const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength; const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header

) + fHeader->sizeofcmds(); @@ -318,6 +333,10 @@ DyldInfoPrinter::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLen if ( segCmd->vmaddr() > 0x100000000ULL ) fWriteableSegmentWithAddrOver4G = true; } + const macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + const macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) + fSections.push_back(sect); } break; case LC_LOAD_DYLIB: @@ -704,9 +723,10 @@ void DyldInfoPrinter::printRebaseInfoOpcodes() } else { printf("rebase opcodes:\n"); - const uint8_t* p = (uint8_t*)fHeader + fInfo->rebase_off(); - const uint8_t* end = &p[fInfo->rebase_size()]; - + const uint8_t* start = (uint8_t*)fHeader + fInfo->rebase_off(); + const uint8_t* end = &start[fInfo->rebase_size()]; + const uint8_t* p = start; + uint8_t type = 0; uint64_t address = fBaseAddress; uint32_t count; @@ -716,44 +736,45 @@ void DyldInfoPrinter::printRebaseInfoOpcodes() while ( !done && (p < end) ) { uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; uint8_t opcode = *p & REBASE_OPCODE_MASK; + uint32_t opcodeOffset = p-start; ++p; switch (opcode) { case REBASE_OPCODE_DONE: done = true; - printf("REBASE_OPCODE_DONE()\n"); + printf("0x%04X REBASE_OPCODE_DONE()\n", opcodeOffset); break; case REBASE_OPCODE_SET_TYPE_IMM: type = immediate; - printf("REBASE_OPCODE_SET_TYPE_IMM(%d)\n", type); + printf("0x%04X REBASE_OPCODE_SET_TYPE_IMM(%d)\n", opcodeOffset, type); break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segmentIndex = immediate; address = read_uleb128(p, end); - printf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", segmentIndex, address); + printf("0x%04X REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(%d, 0x%08llX)\n", opcodeOffset, segmentIndex, address); break; case REBASE_OPCODE_ADD_ADDR_ULEB: address = read_uleb128(p, end); - printf("REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", address); + printf("0x%04X REBASE_OPCODE_ADD_ADDR_ULEB(0x%0llX)\n", opcodeOffset, address); break; case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: address = immediate*sizeof(pint_t); - printf("REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", address); + printf("0x%04X REBASE_OPCODE_ADD_ADDR_IMM_SCALED(0x%0llX)\n", opcodeOffset, address); break; case REBASE_OPCODE_DO_REBASE_IMM_TIMES: - printf("REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", immediate); + printf("0x%04X REBASE_OPCODE_DO_REBASE_IMM_TIMES(%d)\n", opcodeOffset, immediate); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: count = read_uleb128(p, end); - printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", count); + printf("0x%04X REBASE_OPCODE_DO_REBASE_ULEB_TIMES(%d)\n", opcodeOffset, count); break; case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: skip = read_uleb128(p, end) + sizeof(pint_t); - printf("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", skip); + printf("0x%04X REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB(%d)\n", opcodeOffset, skip); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); - printf("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", count, skip); + printf("0x%04X REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB(%d, %d)\n", opcodeOffset, count, skip); break; default: throwf("bad rebase opcode %d", *p); @@ -1485,102 +1506,126 @@ void DyldInfoPrinter::printExportInfoNodes() template -const uint8_t* DyldInfoPrinter::printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind) +const uint8_t* DyldInfoPrinter::printSharedRegionV1InfoForEachULEB128Address(const uint8_t* p, const uint8_t* end, uint8_t kind) { const char* kindStr = "??"; - switch (kind) { - case 1: + switch (kind ) { + case DYLD_CACHE_ADJ_V1_POINTER_32: kindStr = "32-bit pointer"; break; - case 2: + case DYLD_CACHE_ADJ_V1_POINTER_64: kindStr = "64-bit pointer"; break; - case 3: -#if SUPPORT_ARCH_arm64 - if ( fHeader->cputype() == CPU_TYPE_ARM64 ) - kindStr = "arm64 ADRP"; - else -#endif - kindStr = "ppc hi16"; - break; - case 4: - kindStr = "32-bit offset to IMPORT"; - break; - case 5: - kindStr = "thumb2 movw"; - break; - case 6: - kindStr = "ARM movw"; + case DYLD_CACHE_ADJ_V1_ADRP: + kindStr = "arm64 ADRP"; break; - case 0x10: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+0: kindStr = "thumb2 movt low high 4 bits=0"; break; - case 0x11: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+1: kindStr = "thumb2 movt low high 4 bits=1"; break; - case 0x12: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+2: kindStr = "thumb2 movt low high 4 bits=2"; break; - case 0x13: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+3: kindStr = "thumb2 movt low high 4 bits=3"; break; - case 0x14: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+4: kindStr = "thumb2 movt low high 4 bits=4"; break; - case 0x15: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+5: kindStr = "thumb2 movt low high 4 bits=5"; break; - case 0x16: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+6: kindStr = "thumb2 movt low high 4 bits=6"; break; - case 0x17: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+7: kindStr = "thumb2 movt low high 4 bits=7"; break; - case 0x18: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+8: kindStr = "thumb2 movt low high 4 bits=8"; break; - case 0x19: + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+9: kindStr = "thumb2 movt low high 4 bits=9"; break; - case 0x1A: - kindStr = "thumb2 movt low high 4 bits=0xA"; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+10: + kindStr = "thumb2 movt low high 4 bits=10"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+11: + kindStr = "thumb2 movt low high 4 bits=11"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+12: + kindStr = "thumb2 movt low high 4 bits=12"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+13: + kindStr = "thumb2 movt low high 4 bits=13"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+14: + kindStr = "thumb2 movt low high 4 bits=14"; + break; + case DYLD_CACHE_ADJ_V1_ARM_THUMB_MOVT+15: + kindStr = "thumb2 movt low high 4 bits=15"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+0: + kindStr = "arm movt low high 4 bits=0"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+1: + kindStr = "arm movt low high 4 bits=1"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+2: + kindStr = "arm movt low high 4 bits=2"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+3: + kindStr = "arm movt low high 4 bits=3"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+4: + kindStr = "arm movt low high 4 bits=4"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+5: + kindStr = "arm movt low high 4 bits=5"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+6: + kindStr = "arm movt low high 4 bits=6"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+7: + kindStr = "arm movt low high 4 bits=7"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+8: + kindStr = "arm movt low high 4 bits=8"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+9: + kindStr = "arm movt low high 4 bits=9"; + break; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+10: + kindStr = "arm movt low high 4 bits=10"; break; - case 0x1B: - kindStr = "thumb2 movt low high 4 bits=0xB"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+11: + kindStr = "arm movt low high 4 bits=11"; break; - case 0x1C: - kindStr = "thumb2 movt low high 4 bits=0xC"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+12: + kindStr = "arm movt low high 4 bits=12"; break; - case 0x1D: - kindStr = "thumb2 movt low high 4 bits=0xD"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+13: + kindStr = "arm movt low high 4 bits=13"; break; - case 0x1E: - kindStr = "thumb2 movt low high 4 bits=0xE"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+14: + kindStr = "arm movt low high 4 bits=14"; break; - case 0x1F: - kindStr = "thumb2 movt low high 4 bits=0xF"; + case DYLD_CACHE_ADJ_V1_ARM_MOVT+15: + kindStr = "arm movt low high 4 bits=15"; break; + default: + kindStr = "<>"; } uint64_t address = 0; uint64_t delta = 0; - uint32_t shift = 0; - bool more = true; do { - uint8_t byte = *p++; - delta |= ((byte & 0x7F) << shift); - shift += 7; - if ( byte < 0x80 ) { - if ( delta != 0 ) { - address += delta; - printf("0x%0llX %s\n", address+fBaseAddress, kindStr); - delta = 0; - shift = 0; - } - else { - more = false; - } - } - } while (more); + delta = read_uleb128(p, end); + address += delta; + printf("0x%0llX %s\n", address+fBaseAddress, kindStr); + } while (delta); + return p; } @@ -1593,14 +1638,96 @@ void DyldInfoPrinter::printSharedRegionInfo() else { const uint8_t* infoStart = (uint8_t*)fHeader + fSharedRegionInfo->dataoff(); const uint8_t* infoEnd = &infoStart[fSharedRegionInfo->datasize()]; - for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { - uint8_t kind = *p++; - p = this->printSharedRegionInfoForEachULEB128Address(p, kind); + if ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT ) { + ++infoStart; + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + const uint8_t* p = infoStart; + uint64_t sectionCount = read_uleb128(p, infoEnd); + for (uint64_t i=0; i < sectionCount; ++i) { + uint64_t fromSectionIndex = read_uleb128(p, infoEnd); + uint64_t toSectionIndex = read_uleb128(p, infoEnd); + uint64_t toOffsetCount = read_uleb128(p, infoEnd); + const macho_section

* fromSection = fSections[fromSectionIndex]; + const macho_section

* toSection = fSections[toSectionIndex]; + printf("from sect=%s, to sect=%s, count=%lld:\n", fromSection->sectname(), toSection->sectname(), toOffsetCount); + uint64_t toSectionOffset = 0; + const char* lastFromSymbol = NULL; + for (uint64_t j=0; j < toOffsetCount; ++j) { + uint64_t toSectionDelta = read_uleb128(p, infoEnd); + uint64_t fromOffsetCount = read_uleb128(p, infoEnd); + toSectionOffset += toSectionDelta; + for (uint64_t k=0; k < fromOffsetCount; ++k) { + uint64_t kind = read_uleb128(p, infoEnd); + uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); + uint64_t fromSectionOffset = 0; + for (uint64_t l=0; l < fromSectDeltaCount; ++l) { + uint64_t delta = read_uleb128(p, infoEnd); + fromSectionOffset += delta; + uint64_t symbolOffset; + const char* s = closestSymbolNameForAddress(fromSection->addr()+fromSectionOffset, &symbolOffset, fromSectionIndex); + if ( (s != lastFromSymbol) && (s != NULL) ) + printf(" %s:\n", s); + const char* toSymbol = closestSymbolNameForAddress(toSection->addr()+toSectionOffset, &symbolOffset, toSectionIndex); + printf(" from addr=0x%0llX %s to addr=0x%0llX", fromSection->addr()+fromSectionOffset, sharedRegionKindName(kind), toSection->addr()+toSectionOffset); + if ( toSymbol != NULL ) { + if ( symbolOffset == 0 ) + printf(" (%s)", toSymbol); + else + printf(" (%s + %lld)", toSymbol, symbolOffset); + } + printf("\n"); + lastFromSymbol = s; + } + } + } + } + } + else { + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { + uint8_t kind = *p++; + p = this->printSharedRegionV1InfoForEachULEB128Address(p, infoEnd, kind); + } } + } +} +template +const char* DyldInfoPrinter::sharedRegionKindName(uint8_t kind) +{ + switch (kind) { + default: + return "<>"; + case DYLD_CACHE_ADJ_V2_POINTER_32: + return "pointer32"; + case DYLD_CACHE_ADJ_V2_POINTER_64: + return "pointer64"; + case DYLD_CACHE_ADJ_V2_DELTA_32: + return "delta32"; + case DYLD_CACHE_ADJ_V2_DELTA_64: + return "delta64"; + case DYLD_CACHE_ADJ_V2_ARM64_ADRP: + return "adrp"; + case DYLD_CACHE_ADJ_V2_ARM64_OFF12: + return "off12"; + case DYLD_CACHE_ADJ_V2_ARM64_BR26: + return "br26"; + case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT: + return "movw/movt"; + case DYLD_CACHE_ADJ_V2_ARM_BR24: + return "br24"; + case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT: + return "movw/movt"; + case DYLD_CACHE_ADJ_V2_THUMB_BR22: + return "br22"; + case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: + return "off32"; } } + #if SUPPORT_ARCH_arm_any template <> void DyldInfoPrinter::printFunctionStartLine(uint64_t addr) @@ -1686,7 +1813,6 @@ void DyldInfoPrinter::printDRInfo() const uint8_t* start = ((uint8_t*)fHeader + fDRInfo->dataoff()); //const uint8_t* end = ((uint8_t*)fHeader + fDRInfo->dataoff() + fDRInfo->datasize()); typedef Security::SuperBlob DRListSuperBlob; - typedef Security::SuperBlob InternalRequirementsSetBlob; const DRListSuperBlob* topBlob = (DRListSuperBlob*)start; if ( topBlob->validateBlob(fDRInfo->datasize()) ) { if ( topBlob->count() == fDylibLoadCommands.size() ) { @@ -1951,37 +2077,65 @@ void DyldInfoPrinter::printSymbolTableExportInfo() } template -const char* DyldInfoPrinter::symbolNameForAddress(uint64_t addr) +const char* DyldInfoPrinter::closestSymbolNameForAddress(uint64_t addr, uint64_t* offset, uint8_t sectIndex) { + const macho_nlist

* bestSymbol = NULL; if ( fDynamicSymbolTable != NULL ) { - // find exact match in globals - const macho_nlist

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

* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) { - if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { - return &fStrings[sym->n_strx()]; + // find closest match in globals + const macho_nlist

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

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

* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type() & N_TYPE) == N_SECT ) { + if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) { + if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) ) + bestSymbol = s; + } } } - // find exact match in local symbols - const macho_nlist

* lastLocal = &fSymbols[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()]; - for (const macho_nlist

* sym = &fSymbols[fDynamicSymbolTable->ilocalsym()]; sym < lastLocal; ++sym) { - if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { - return &fStrings[sym->n_strx()]; + + // find closest match in locals + const macho_nlist

* const localsStart = &fSymbols[fDynamicSymbolTable->ilocalsym()]; + const macho_nlist

* const localsEnd = &localsStart[fDynamicSymbolTable->nlocalsym()]; + for (const macho_nlist

* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type() & N_TYPE) == N_SECT) && ((s->n_type() & N_STAB) == 0) ) { + if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) { + if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) ) + bestSymbol = s; + } } } } else { - // find exact match in all symbols - const macho_nlist

* lastSym = &fSymbols[fSymbolCount]; - for (const macho_nlist

* sym = &fSymbols[0]; sym < lastSym; ++sym) { - if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) { - return &fStrings[sym->n_strx()]; + // find closest match in locals + const macho_nlist

* const allStart = &fSymbols[0]; + const macho_nlist

* const allEnd = &fSymbols[fSymbolCount]; + for (const macho_nlist

* s = allStart; s < allEnd; ++s) { + if ( ((s->n_type() & N_TYPE) == N_SECT) && ((s->n_type() & N_STAB) == 0) ) { + if ( (s->n_value() <= addr) && ((s->n_sect() == sectIndex) || (sectIndex ==0)) ) { + if ( (bestSymbol == NULL) || (bestSymbol->n_value() < s->n_value()) ) + bestSymbol = s; + } } } } + if ( bestSymbol != NULL ) { + *offset = addr - bestSymbol->n_value(); + return &fStrings[bestSymbol->n_strx()]; + } + *offset = 0; + return NULL; +} +template +const char* DyldInfoPrinter::symbolNameForAddress(uint64_t addr) +{ + uint64_t offset; + const char* s = closestSymbolNameForAddress(addr, &offset); + if ( (offset == 0) && (s != NULL) ) + return s; return "?"; } - + template void DyldInfoPrinter::printClassicBindingInfo() { @@ -2223,7 +2377,7 @@ static void usage() "\t-opcodes print opcodes used to generate the rebase and binding information\n" "\t-function_starts print table of function start addresses\n" "\t-export_dot print a GraphViz .dot file of the exported symbols trie\n" - "\t-data_in_code print any data-in-code inforamtion\n" + "\t-data_in_code print any data-in-code information\n" ); } diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 545b4a7..f0cbbf1 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -55,7 +55,7 @@ ifeq ($(ARCH),ppc) SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk endif -CC = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra} +CC = $(shell xcrun -find clang) -arch ${ARCH} ${SDKExtra} -mmacosx-version-min=10.8 CCFLAGS = -Wall ASMFLAGS = VERSION_NEW_LINKEDIT = -mmacosx-version-min=10.6 @@ -65,7 +65,7 @@ LD_NEW_LINKEDIT = -macosx_version_min 10.6 CXX = $(shell xcrun -find clang++) -arch ${ARCH} ${SDKExtra} CXXFLAGS = -Wall -stdlib=libc++ -IOS_SDK = $(shell xcodebuild -sdk iphoneos8.0.internal -version Path 2>/dev/null) +IOS_SDK = $(shell xcodebuild -sdk iphoneos.internal -version Path 2>/dev/null) ifeq ($(ARCH),armv6) LDFLAGS := -syslibroot $(IOS_SDK) @@ -127,16 +127,13 @@ endif ifeq ($(ARCH),arm64) LDFLAGS := -syslibroot $(IOS_SDK) - CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) - #CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) - #CC = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.0.xctoolchain/usr/bin/clang-loh -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) - CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=7.0 -isysroot $(IOS_SDK) + CC = $(shell xcrun --sdk iphoneos.internal -find clang) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=9.0 -isysroot $(IOS_SDK) + CXX = $(shell xcrun --sdk iphoneos.internal -find clang++) -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=9.0 -isysroot $(IOS_SDK) VERSION_NEW_LINKEDIT = -miphoneos-version-min=7.0 VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0 LD_SYSROOT = -syslibroot $(IOS_SDK) LD_NEW_LINKEDIT = -ios_version_min 7.0 - OTOOL = /Applications/Xcode.app/Contents/Developer/Toolchains/iOS7.1.xctoolchain/usr/bin/otool - #OTOOL = $(shell xcrun --sdk iphoneos.internal -find otool) + OTOOL = $(shell xcrun --sdk iphoneos.internal -find otool) else FILEARCH = $(ARCH) endif diff --git a/unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile b/unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile new file mode 100644 index 0000000..0949507 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-live-if-ref-live/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Sanity check -dead_strip does not remove initializers and terminators +# + + +run: all + +all: + ${CC} ${CCFLAGS} main.c -c -o main.o + ${CC} ${CCFLAGS} main.o -dead_strip -o main + ${FAIL_IF_BAD_MACHO} main + nm main | grep _aa | ${FAIL_IF_EMPTY} + nm main | grep _aaInfo | ${FAIL_IF_EMPTY} + nm main | grep _bb | ${FAIL_IF_STDIN} + ${LD} -r main.o -o main-r.o + ${CC} ${CCFLAGS} main-r.o -dead_strip -o main-r + nm main-r | grep _aa | ${FAIL_IF_EMPTY} + nm main-r | grep _aaInfo | ${FAIL_IF_EMPTY} + nm main-r | grep _bb | ${FAIL_IF_STDIN} + ${PASS_IFF_GOOD_MACHO} main-r + +clean: + rm -rf main.o main main-r.o main-r diff --git a/unit-tests/test-cases/dead_strip-live-if-ref-live/main.c b/unit-tests/test-cases/dead_strip-live-if-ref-live/main.c new file mode 100644 index 0000000..e038a28 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-live-if-ref-live/main.c @@ -0,0 +1,29 @@ +#include + +int aa = 10; +int bb = 20; +int cc = 30; + + +int main() +{ + printf("%p %p\n", &aa, &cc); + return 0; +} + + +struct MetaData { + void* addr; + unsigned long size; + const char* name; +}; + + +#define META_DATA(__x) \ + __attribute__((used, section("__DATA,__meta,regular,live_support"))) \ + static struct MetaData __x##Info = { &__x, sizeof(__x), #__x }; + + +META_DATA(aa); +META_DATA(bb); +META_DATA(cc); diff --git a/unit-tests/test-cases/tlv-basic/get.s b/unit-tests/test-cases/tlv-basic/get.s index 9255841..b0eb6de 100644 --- a/unit-tests/test-cases/tlv-basic/get.s +++ b/unit-tests/test-cases/tlv-basic/get.s @@ -159,4 +159,117 @@ _get_d: #endif + +#if __arm__ + + # _a is global TLV + .tlv + .globl _a +_a: .long __tlv_bootstrap + .long 0 + .long _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .long __tlv_bootstrap + .long 0 + .long _b$tlv$init + + # _c is a non-external TLV + .tlv +_c: .long __tlv_bootstrap + .long 0 + .long _c$tlv$init + + # _d is a non-external TLV + .tlv +_d: .long __tlv_bootstrap + .long 0 + .long _d$tlv$init + + + .text + + .globl _get_a + .align 2 + .code 16 + .thumb_func _get_a +_get_a: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(La-(LPC0_0+4)) + movt r0, :upper16:(La-(LPC0_0+4)) +LPC0_0: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .globl _get_b + .align 2 + .code 16 + .thumb_func _get_b +_get_b: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(Lb-(LPC0_1+4)) + movt r0, :upper16:(Lb-(LPC0_1+4)) +LPC0_1: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .globl _get_c + .align 2 + .code 16 + .thumb_func _get_c +_get_c: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(Lc-(LPC0_2+4)) + movt r0, :upper16:(Lc-(LPC0_2+4)) +LPC0_2: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .globl _get_d + .align 2 + .code 16 + .thumb_func _get_d +_get_d: + push {r7, lr} + mov r7, sp + movw r0, :lower16:(Ld-(LPC0_3+4)) + movt r0, :upper16:(Ld-(LPC0_3+4)) +LPC0_3: + add r0, pc + ldr r0, [r0] + ldr ip, [r0] + blx ip + pop {r7, pc} + + + .section __DATA,__thread_ptr,thread_local_variable_pointers +La: + .long _a +Lb: + .long _b +Lc: + .long _c +Ld: + .long _d + + +#endif + .subsections_via_symbols diff --git a/unit-tests/test-cases/tlv-basic/main.c b/unit-tests/test-cases/tlv-basic/main.c index fc56a13..b5fd7f6 100644 --- a/unit-tests/test-cases/tlv-basic/main.c +++ b/unit-tests/test-cases/tlv-basic/main.c @@ -22,6 +22,8 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include + // work around until compiler supports __thread extern int* get_a(); extern int* get_b(); @@ -30,10 +32,19 @@ extern int* get_d(); int main() { - get_a(); - get_b(); - get_c(); - get_d(); + int* p; + p = get_a(); + printf("&a=%p, a=%d\n", p, *p); + + p = get_b(); + printf("&b=%p, b=%d\n", p, *p); + + p = get_c(); + printf("&c=%p, c=%d\n", p, *p); + + p = get_d(); + printf("&d=%p, e=%d\n", p, *p); + return 0; } -- 2.45.2