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
.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
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.
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
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
/* 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 */; };
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 */
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ B028FCF01A9E7B4A00E3584B /* bitcode_bundle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bitcode_bundle.h; sourceTree = "<group>"; };
+ B028FCF11A9E7C3F00E3584B /* bitcode_bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitcode_bundle.cpp; sourceTree = "<group>"; };
+ B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Bitcode.hpp; path = src/ld/Bitcode.hpp; sourceTree = "<group>"; };
B3B672411406D42800A376BB /* Snapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Snapshot.cpp; path = src/ld/Snapshot.cpp; sourceTree = "<group>"; 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 = "<group>"; tabWidth = 4; usesTabs = 1; };
B3C7A09914295B9C005FC714 /* compile_stubs */ = {isa = PBXFileReference; lastKnownFileType = text.script.csh; path = compile_stubs; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
F9EA7583097882F3008B4F1D /* debugline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = debugline.h; path = src/ld/debugline.h; sourceTree = "<group>"; 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 = "<group>"; tabWidth = 4; usesTabs = 1; };
+ FA95D6121AB25CF400395811 /* textstub_dylib_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = textstub_dylib_file.cpp; sourceTree = "<group>"; usesTabs = 1; };
+ FA95D6131AB25CF400395811 /* textstub_dylib_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = textstub_dylib_file.hpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
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 */,
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 */,
F9AA650B1051BD2B003E3539 /* passes */,
F9AA65861051E750003E3539 /* parsers */,
F933DC37092A82480083EAC8 /* Architectures.hpp */,
+ B091FB641ABA3AFB00CC8193 /* Bitcode.hpp */,
F9EA7582097882F3008B4F1D /* debugline.c */,
F9EA7583097882F3008B4F1D /* debugline.h */,
B3B672411406D42800A376BB /* Snapshot.cpp */,
);
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 */ = {
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 */,
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;
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)";
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;
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)";
);
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;
F933D92009291AC90083EAC8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
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)",
F933D92109291AC90083EAC8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/src/ld",
"$(DEVELOPER_DIR)/usr/local/include",
+ "$(DT_TOOLCHAIN_DIR)/usr/local/include",
);
INSTALL_PATH = "$(HOME)/bin";
OTHER_CPLUSPLUSFLAGS = (
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)";
);
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;
F9849FFD10B5DE8E009E9878 /* Release-assert */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/src/ld",
"$(DEVELOPER_DIR)/usr/local/include",
+ "$(DT_TOOLCHAIN_DIR)/usr/local/include",
);
INSTALL_PATH = "$(HOME)/bin";
OTHER_CPLUSPLUSFLAGS = (
#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
#define UNWIND_ARM_DWARF_SECTION_OFFSET 0x00FFFFFF
+
+// ( <opcode> (delta-uleb128)+ <zero> )+ <zero>
+#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 :== <new-marker> <count> FromToSection+
+// FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+// ToOffset :== <to-sect-offset-delta> <count> FromOffset+
+// FromOffset :== <kind> <count> <from-sect-offset-delta>
+#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;
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
--- /dev/null
+/* -*- 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 <unistd.h>
+
+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__) */
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 <typename A>
// 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;
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 char*>&) const;
uint8_t* copyOptimizationHintsLoadCommand(uint8_t* p) const;
bool _hasFunctionStartsLoadCommand;
bool _hasDataInCodeLoadCommand;
bool _hasSourceVersionLoadCommand;
- bool _hasDependentDRInfo;
bool _hasOptimizationHints;
uint32_t _dylibLoadCommmandsCount;
uint32_t _allowableClientLoadCommmandsCount;
}
_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();
return count;
}
+template <typename A>
+bool HeaderAndLoadCommandsAtom<A>::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<P>);
+ const char* lastSegName = "";
+ for (std::vector<ld::Internal::FinalSection*>::iterator it = _state.sections.begin(); it != _state.sections.end(); ++it) {
+ if ( strcmp(lastSegName, (*it)->segmentName()) != 0 ) {
+ lastSegName = (*it)->segmentName();
+ cmdOffset += sizeof(macho_segment_command<P>);
+ }
+ if ( strcmp((*it)->segmentName(), "__LLVM") == 0 && strcmp((*it)->sectionName(), "__bundle") == 0 ) {
+ sectOffset = (*it)->fileOffset;
+ sectEnd = (*(it + 1))->fileOffset;
+ cmdEnd = cmdOffset + sizeof(macho_section<P>);
+ return true;
+ }
+ if ( ! (*it)->isSectionHidden() )
+ cmdOffset += sizeof(macho_section<P>);
+ }
+ return false;
+}
template <typename A>
uint64_t HeaderAndLoadCommandsAtom<A>::size() const
}
}
- if ( _hasDependentDRInfo )
- sz += sizeof(macho_linkedit_data_command<P>);
-
if ( _hasOptimizationHints )
sz += sizeof(macho_linkedit_data_command<P>);
}
}
- if ( _hasDependentDRInfo )
- ++count;
-
if ( _hasOptimizationHints )
++count;
SegInfo(const char* n, const Options&);
const char* segName;
uint32_t nonHiddenSectionCount;
+ uint32_t nonSectCreateSections;
uint32_t maxProt;
uint32_t initProt;
std::vector<ld::Internal::FinalSection*> sections;
SegInfo::SegInfo(const char* n, const Options& opts)
- : segName(n), nonHiddenSectionCount(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n))
+ : segName(n), nonHiddenSectionCount(0), nonSectCreateSections(0), maxProt(opts.maxSegProtection(n)), initProt(opts.initialSegProtection(n))
{
}
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:
return S_REGULAR;
case ld::Section::typeDebug:
return S_REGULAR | S_ATTR_DEBUG;
+ case ld::Section::typeSectCreate:
+ return S_REGULAR;
}
return S_REGULAR;
}
}
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
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<P>);
macho_section<P>* msect = (macho_section<P>*)p;
for (std::vector<ld::Internal::FinalSection*>::iterator sit = si.sections.begin(); sit != si.sections.end(); ++sit) {
uint8_t* HeaderAndLoadCommandsAtom<A>::copyVersionLoadCommand(uint8_t* p) const
{
macho_version_min_command<P>* cmd = (macho_version_min_command<P>*)p;
- ld::MacVersionMin macVersion = _options.macosxVersionMin();
- ld::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<P>));
- 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<P>));
- 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<P>));
+ 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<P>));
+ 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<P>));
+ 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<P>));
+ 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<P>));
+ cmd->set_version(_state.minOSVersion);
+ cmd->set_sdk(_options.sdkVersion());
+ break;
+#endif
}
return p + sizeof(macho_version_min_command<P>);
}
template <>
uint32_t HeaderAndLoadCommandsAtom<x86_64>::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 <>
macho_thread_command<P>* cmd = (macho_thread_command<P>*)p;
cmd->set_cmd(LC_UNIXTHREAD);
cmd->set_cmdsize(threadLoadCommandSize());
- cmd->set_flavor(x86_THREAD_STATE64);
- cmd->set_count(x86_THREAD_STATE64_COUNT);
+ cmd->set_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
}
-template <typename A>
-uint8_t* HeaderAndLoadCommandsAtom<A>::copyDependentDRLoadCommand(uint8_t* p) const
-{
- macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)p;
- cmd->set_cmd(LC_DYLIB_CODE_SIGN_DRS);
- cmd->set_cmdsize(sizeof(macho_linkedit_data_command<P>));
- cmd->set_dataoff(_writer.dependentDRsSection->fileOffset);
- cmd->set_datasize(_writer.dependentDRsSection->size);
- return p + sizeof(macho_linkedit_data_command<P>);
-}
-
-
template <typename A>
uint8_t* HeaderAndLoadCommandsAtom<A>::copyOptimizationHintsLoadCommand(uint8_t* p) const
}
}
- if ( _hasDependentDRInfo )
- p = this->copyDependentDRLoadCommand(p);
-
if ( _hasOptimizationHints )
p = this->copyOptimizationHintsLoadCommand(p);
#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"
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);
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() ) {
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:
break;
}
-
// see if it is a static library
::archive::ParserOptions archOpts;
archOpts.objOpts = objOpts;
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);
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
if ( ! dylibReader->installPathVersionSpecific() ) {
+ dylibReader->forEachAtom(handler);
dylibReader->setImplicitlyLinked();
this->addDylib(dylibReader, info);
}
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
ld::archive::File* archiveReader = dynamic_cast<ld::archive::File*>(reader);
if ( dylibReader != NULL ) {
+ dylibReader->forEachAtom(handler);
dylibReader->setImplicitlyLinked();
this->addDylib(dylibReader, info);
}
_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<Options::FileInfo>& files = opts.getInputFiles();
for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) {
int fd = ::open(it->path, O_RDONLY, 0);
if ( fd != -1 ) {
- ssize_t amount = read(fd, buffer, sizeof(buffer));
- ::close(fd);
- if ( amount >= (ssize_t)sizeof(buffer) ) {
- cpu_type_t type;
- cpu_subtype_t subtype;
- if ( mach_o::relocatable::isObjectFile(buffer, &type, &subtype) ) {
- opts.setArchitecture(type, subtype);
- *archName = opts.architectureName();
- return;
+ 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;
+ }
}
}
}
// 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
#include <unistd.h>
#include <vector>
+#include <unordered_map>
#include "Options.h"
#include "ld.hpp"
while( more );
}
+ void append_delta_encoded_uleb128_run(uint64_t start, const std::vector<uint64_t>& locations) {
+ uint64_t lastAddr = start;
+ for(std::vector<uint64_t>::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);
}
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));
std::vector<const ld::Atom*>& exports = this->_writer._exportedAtoms;
uint64_t imageBaseAddress = this->_writer.headerAndLoadCommandsSection->address;
std::vector<mach_o::trie::Entry> entries;
+ unsigned int padding = 0;
entries.reserve(exports.size());
for (std::vector<const ld::Atom*>::const_iterator it = exports.begin(); it != exports.end(); ++it) {
const ld::Atom* atom = *it;
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
// 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));
template <typename A>
-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
};
template <typename A>
-ld::Section SplitSegInfoAtom<A>::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true);
+ld::Section SplitSegInfoV1Atom<A>::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true);
template <>
-void SplitSegInfoAtom<x86_64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
+void SplitSegInfoV1Atom<x86_64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
{
switch (kind) {
case ld::Fixup::kindStoreX86PCRel32:
}
template <>
-void SplitSegInfoAtom<x86>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
+void SplitSegInfoV1Atom<x86>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
{
switch (kind) {
case ld::Fixup::kindStoreLittleEndian32:
}
template <>
-void SplitSegInfoAtom<arm>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
+void SplitSegInfoV1Atom<arm>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
{
switch (kind) {
case ld::Fixup::kindStoreLittleEndian32:
#if SUPPORT_ARCH_arm64
template <>
-void SplitSegInfoAtom<arm64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
+void SplitSegInfoV1Atom<arm64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
{
switch (kind) {
case ld::Fixup::kindStoreARM64Page21:
#endif
template <typename A>
-void SplitSegInfoAtom<A>::uleb128EncodeAddresses(const std::vector<uint64_t>& locations) const
+void SplitSegInfoV1Atom<A>::uleb128EncodeAddresses(const std::vector<uint64_t>& locations) const
{
pint_t addr = this->_options.baseAddress();
for(typename std::vector<uint64_t>::const_iterator it = locations.begin(); it != locations.end(); ++it) {
template <typename A>
-void SplitSegInfoAtom<A>::encode() const
+void SplitSegInfoV1Atom<A>::encode() const
{
// sort into group by pointer adjustment kind
std::vector<OutputFile::SplitSegInfoEntry>& info = this->_writer._splitSegInfos;
for (std::vector<OutputFile::SplitSegInfoEntry>::const_iterator it = info.begin(); it != info.end(); ++it) {
- this->addSplitSegInfo(it->address, it->kind, it->extra);
+ this->addSplitSegInfo(it->fixupAddress, it->kind, it->extra);
}
// delta compress runs of addresses
_64bitPointerLocations.clear();
}
+
+template <typename A>
+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 :== <count> FromToSection+
+ // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+ // ToOffset :== <to-sect-offset-delta> <count> FromOffset+
+ // FromOffset :== <kind> <count> <from-sect-offset-delta>
+
+ typedef uint32_t SectionIndexes;
+ typedef std::map<uint8_t, std::vector<uint64_t> > FromOffsetMap;
+ typedef std::map<uint64_t, FromOffsetMap> ToOffsetMap;
+ typedef std::map<SectionIndexes, ToOffsetMap> WholeMap;
+
+
+ static ld::Section _s_section;
+};
+
+template <typename A>
+ld::Section SplitSegInfoV2Atom<A>::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true);
+
+
+template <typename A>
+void SplitSegInfoV2Atom<A>::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 :== <count> 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 :== <from-sect-index> <to-sect-index> <count> 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 :== <to-sect-offset-delta> <count> FromOffset+
+ this->_encodedData.append_uleb128(toSectionOffset - lastToOffset);
+ this->_encodedData.append_uleb128(fromOffsets.size());
+ for (auto& kindAndOffsets : fromOffsets) {
+ uint8_t kind = kindAndOffsets.first;
+ std::vector<uint64_t>& fromOffsets = kindAndOffsets.second;
+ // FromOffset :== <kind> <count> <from-sect-offset-delta>
+ 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 <typename A>
class FunctionStartsAtom : public LinkEditAtom
{
-// <rdar://problem/7209249> linker needs to cache "Designated Requirements" in linked binary
-template <typename A>
-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 <typename A>
-ld::Section DependentDRAtom<A>::_s_section("__LINKEDIT", "__dependentDR", ld::Section::typeLinkEdit, true);
-
-
-template <typename A>
-void DependentDRAtom<A>::encode() const
-{
- Security::SuperBlobCore<Security::SuperBlob<Security::kSecCodeMagicDRList>, Security::kSecCodeMagicDRList, uint32_t>::Maker maker;
-
- uint32_t index = 0;
- for(std::vector<ld::dylib::File*>::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 ) {
- // <rdar://problem/11315321> 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<Security::kSecCodeMagicDRList>* 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 <typename A>
class OptimizationHintsAtom : public LinkEditAtom
{
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;
#include <Availability.h>
#include <vector>
+#include <map>
+#include <sstream>
#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();
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),
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);
bool Options::printWhyLive(const char* symbolName) const
{
- return ( fWhyLive.find(symbolName) != fWhyLive.end() );
+ return fWhyLive.contains(symbolName);
}
{
// iPhoneOS always uses same protection for max and initial
// <rdar://problem/11663436> 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<Options::SegmentProtect>::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) {
return 0;
}
+bool Options::segmentOrderAfterFixedAddressSegment(const char* segName) const
+{
+ bool nowPinned = false;
+ for (std::vector<const char*>::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()
{
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) ) {
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);
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);
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;
}
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) )
Options::FileInfo Options::findFramework(const char* rootName, const char* suffix) const
{
- for (std::vector<const char*>::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;
}
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 char*>::const_iterator it = fSDKPaths.begin(); it != fSDKPaths.end(); it++) {
- // ??? Shouldn't we be using String here?
- const char* sdkPathDir = *it;
- const int sdkPathDirLen = strlen(sdkPathDir);
- 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;
}
// don't need to try variations, just paths. We do need to add the additional bits
// onto the framework path though.
if ( isFramework ) {
- for (std::vector<const char*>::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
- // <rdar://problem/5427952> 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) );
+ // <rdar://problem/5427952> 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 char*>::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;
}
}
}
+std::vector<const char*> Options::exportsData() const
+{
+ return fExportSymbols.data();
+}
+
+
+std::vector<const char*> Options::SetWithWildcards::data() const
+{
+ std::vector<const char*> data;
+ for (NameSet::iterator it=regularBegin(); it != regularEnd(); ++it) {
+ data.push_back(*it);
+ }
+ for (std::vector<const char*>::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
{
minorVersion = 255;
}
fMacVersionMin = (ld::MacVersionMin)(0x000A0000 | (minorVersion << 8));
+ fPlatform = kPlatformOSX;
}
else {
warning("unknown option to -macosx_version_min, not 10.x");
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)
{
::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);
}
}
+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<std::string> Options::writeBitcodeLinkOptions() const
+{
+ std::vector<std::string> 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 char*>::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.
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 )
}
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;
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = true;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-lto_library") == 0 ) {
snapshotFileArgIndex = 1;
// 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 ) {
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.
warning("-seg1addr not %lld byte aligned, rounding up", fSegmentAlignment);
fBaseAddress = temp;
}
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-e") == 0 ) {
fEntryName = argv[++i];
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];
// 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 ) {
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 )
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;
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;
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 ) {
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 ) {
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)
info.ordinal = ld::File::Ordinal::makeArgOrdinal((uint16_t)i);
addLibrary(info);
fUsingLazyDylibLinking = true;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-framework") == 0 ) {
snapshotArgCount = 0;
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()
}
else if ( strcmp(arg, "-undefined") == 0 ) {
setUndefinedTreatment(argv[++i]);
+ cannotBeUsedWithBitcode(arg);
}
// Debugging output flag.
else if ( strcmp(arg, "-arch_multiple") == 0 ) {
case kWarning:
fWarnTextRelocs = true;
fAllowTextRelocs = true;
+ cannotBeUsedWithBitcode(arg);
break;
case kSuppress:
fWarnTextRelocs = false;
fAllowTextRelocs = true;
+ cannotBeUsedWithBitcode(arg);
break;
case kError:
fWarnTextRelocs = false;
// 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);
// 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 ) {
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.
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 ) {
if ( name == NULL )
throw "-seg_addr_table missing argument";
fSegAddrTablePath = name;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-seg_addr_table_filename") == 0 ) {
warnObsolete(arg);
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];
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 <address>";
fStackAddr = parseAddress(address);
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-stack_size") == 0 ) {
const char* size = argv[++i];
}
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 <segment> <section> <file-path>";
addSectionAlignment(argv[i+1], argv[i+2], argv[i+3]);
i += 3;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-sectorder_detail") == 0 ) {
warnObsolete(arg);
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;
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);
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;
if ( name == NULL )
throw "-umbrella missing argument";
fUmbrellaName = name;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-allowable_client") == 0 ) {
const char* name = argv[++i];
throw "-allowable_client missing argument";
fAllowableClients.push_back(name);
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-client_name") == 0 ) {
const char* name = argv[++i];
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;
++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;
if ( name == NULL )
throw "-dtrace missing argument";
fDtraceScriptName = name;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-root_safe") == 0 ) {
fRootSafe = true;
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 )
}
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)
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)
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)
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)
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)
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)
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;
// 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;
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];
}
else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) {
fObjcGcCompaction = true;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-objc_gc") == 0 ) {
fObjCGc = true;
warning("-objc_gc overriding -objc_gc_only");
fObjCGcOnly = false;
}
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-objc_gc_only") == 0 ) {
fObjCGcOnly = true;
warning("-objc_gc_only overriding -objc_gc");
fObjCGc = false;
}
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-demangle") == 0 ) {
fDemangle = true;
else if ( strcmp(arg, "-no_version_load_command") == 0 ) {
fVersionLoadCommandForcedOff = true;
fVersionLoadCommandForcedOn = false;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-function_starts") == 0 ) {
fFunctionStartsForcedOn = true;
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;
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 <symbol>";
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 <symbol>";
fForceNotWeakSymbols.insert(symbol);
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-reexported_symbols_list") == 0 ) {
snapshotFileArgIndex = 1;
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];
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];
}
else if ( strcmp(arg, "-no_source_version") == 0 ) {
fSourceVersionLoadCommandForceOff = true;
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-sdk_version") == 0 ) {
const char* vers = argv[++i];
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;
}
}
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;
throw "-rename_section missing <segment> <section> <segment> <section>";
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 <existing-segment> <new-segment>";
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 <segment> <symbol-list-file>";
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 <segment> <symbol-list-file>";
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
break;
}
}
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-section_order") == 0 ) {
// ex: -section_order __DATA __data:__const:__nl_pointers
break;
}
}
+ cannotBeUsedWithBitcode(arg);
}
else if ( strcmp(arg, "-application_extension") == 0 ) {
fMarkAppExtensionSafe = true;
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 <symbol-list-file>";
+ 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, ':');
throw "-dependency_info missing <path>";
fDependencyInfoPath = path;
}
+ else if ( strcmp(argv[i], "-bitcode_bundle") == 0 ) {
+ fBundleBitcode = true;
+ }
}
int standardLibraryPathsStartIndex = libraryPaths.size();
int standardFrameworkPathsStartIndex = frameworkPaths.size();
}
// 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 ) {
setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
#else
warning("-macosx_version_min not specified, assuming 10.6");
- fMacVersionMin = ld::mac10_6;
+ setMacOSXVersionMin("10.6");
#endif
}
break;
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;
// 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;
}
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;
// 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;
// <rdar://problem/10111122> Enable dyld to be put into the dyld shared cache
fSharedRegionEligible = true;
}
-
+
+ // <rdar://problem/18719327> 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 ) {
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
// <rdar://problem/16293398> 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
if ( (fArchitecture == CPU_TYPE_ARM)
&& fArchSupportsThumb2
&& (fOutputKind == kDynamicExecutable)
- && (fIOSVersionMin >= ld::iOS_4_3) ) {
+ && min_iOS(ld::iOS_4_3) ) {
fPositionIndependentExecutable = true;
}
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;
}
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();
// allow trie based absolute symbols if targeting new enough OS
if ( fMakeCompressedDyldInfo ) {
if ( minOS(ld::mac10_9, ld::iOS_7_0) ) {
- // <rdar://problem/13179029> Allow absolute symbols in export trie for device but not simulator
- if ( !fTargetIOSSimulator )
- fAbsoluteSymbols = true;
+ fAbsoluteSymbols = true;
}
}
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:
// <rdar://problem/14676611> 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;
fBaseAddress = alignedBaseAddress;
}
+ // If -dirty_data_list not specified, look in $SDKROOT/AppleInternal/DirtyDataFiles/<dylib>.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()
throw "-segment_order can only used used with -preload output";
// <rdar://problem/17598404> warn if building an embedded iOS dylib for pre-iOS 8
+ // <rdar://problem/18935714> 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");
}
}
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
#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)));
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:
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; }
bool allGlobalsAreDeadStripRoots() const;
bool shouldExport(const char*) const;
bool shouldReExport(const char*) const;
+ std::vector<const char*> exportsData() const;
bool ignoreOtherArchInputFiles() const { return fIgnoreOtherArchFiles; }
bool traceDylibs() const { return fTraceDylibs; }
bool traceArchives() const { return fTraceArchives; }
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; }
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; }
const std::vector<DylibOverride>& dylibOverrides() const { return fDylibOverrides; }
const char* generatedMapPath() const { return fMapPath; }
bool positionIndependentExecutable() const { return fPositionIndependentExecutable; }
- Options::FileInfo findFileUsingPaths(const char* path) const;
+ 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); }
bool needsUnwindInfoSection() const { return fAddCompactUnwindEncoding; }
const std::vector<const char*>& llvmOptions() const{ return fLLVMOptions; }
const std::vector<const char*>& segmentOrder() const{ return fSegmentOrder; }
+ bool segmentOrderAfterFixedAddressSegment(const char* segName) const;
const std::vector<const char*>* sectionOrder(const char* segName) const;
const std::vector<const char*>& dyldEnvironExtras() const{ return fDyldEnvironExtras; }
const std::vector<const char*>& astFilePaths() const{ return fASTFilePaths; }
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; }
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;
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; }
const std::vector<SegmentRename>& 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<const char*>& sdkPaths() const { return fSDKPaths; }
+ std::vector<std::string> writeBitcodeLinkOptions() const;
+ std::string getSDKVersionStr() const;
+ std::string getPlatformStr() const;
private:
typedef std::unordered_map<const char*, unsigned int, ld::CStringHash, ld::CStringEquals> NameToOrder;
NameSet::iterator regularBegin() const { return fRegular.begin(); }
NameSet::iterator regularEnd() const { return fRegular.end(); }
void remove(const NameSet&);
+ std::vector<const char*> data() const;
private:
static bool hasWildCards(const char*);
bool wildCardMatch(const char* pattern, const char* candidate) const;
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);
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);
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<SymbolsMove>& list, const char* optionName);
+ void cannotBeUsedWithBitcode(const char* arg);
// ObjectFile::ReaderOptions fReaderOptions;
bool fStatistics;
bool fPrintOptions;
bool fSharedRegionEligible;
+ bool fSharedRegionEligibleForceOff;
bool fPrintOrderFileStatistics;
bool fReadOnlyx86Stubs;
bool fPositionIndependentExecutable;
bool fKextsUseStubs;
bool fUsingLazyDylibLinking;
bool fEncryptable;
+ bool fEncryptableForceOn;
+ bool fEncryptableForceOff;
bool fOrderData;
bool fMarkDeadStrippableDylib;
bool fMakeCompressedDyldInfo;
bool fSourceVersionLoadCommand;
bool fSourceVersionLoadCommandForceOn;
bool fSourceVersionLoadCommandForceOff;
- bool fDependentDRInfo;
- bool fDependentDRInfoForcedOn;
- bool fDependentDRInfoForcedOff;
bool fTargetIOSSimulator;
bool fExportDynamic;
bool fAbsoluteSymbols;
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<AliasPair> fAliases;
std::vector<const char*> fInitialUndefines;
NameSet fAllowedUndefined;
- NameSet fWhyLive;
+ SetWithWildcards fWhyLive;
std::vector<ExtraSection> fExtraSections;
std::vector<SectionAlignment> fSectionAlignments;
std::vector<OrderedSymbol> fOrderedSymbols;
#include <list>
#include <algorithm>
#include <unordered_set>
+#include <utility>
#include <CommonCrypto/CommonDigest.h>
#include <AvailabilityMacros.h>
#include "LinkEdit.hpp"
#include "LinkEditClassic.hpp"
-
namespace ld {
namespace tool {
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),
_hasSplitSegInfo(opts.sharedRegionEligible()),
_hasFunctionStartsInfo(opts.addFunctionStarts()),
_hasDataInCodeInfo(opts.addDataInCodeInfo()),
- _hasDependentDRInfo(opts.needsDependentDRInfo()),
_hasDynamicSymbolTable(true),
_hasLocalRelocations(!opts.makeCompressedDyldInfo()),
_hasExternalRelocations(!opts.makeCompressedDyldInfo()),
_splitSegInfoAtom(NULL),
_functionStartsAtom(NULL),
_dataInCodeAtom(NULL),
- _dependentDRInfoAtom(NULL),
_optimizationHintsAtom(NULL)
{
}
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);
_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);
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;
return !mustBeGOT;
case ld::Fixup::kindStoreARM64GOTLoadPage21:
case ld::Fixup::kindStoreARM64GOTLeaPage21:
+ case ld::Fixup::kindStoreARM64TLVPLoadPage21:
+ case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPage21:
return true;
default:
break;
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;
return !mustBeGOT;
case ld::Fixup::kindStoreARM64GOTLoadPageOff12:
case ld::Fixup::kindStoreARM64GOTLeaPageOff12:
+ case ld::Fixup::kindStoreARM64TLVPLoadPageOff12:
+ case ld::Fixup::kindStoreARM64TLVPLoadNowLeaPageOff12:
return true;
default:
break;
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());
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);
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));
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() )
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);
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));
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
// 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) ) {
LOH_ASSERT(infoC.fixup == NULL);
LOH_ASSERT(infoA.target == infoB.target);
LOH_ASSERT(infoA.targetAddress == infoB.targetAddress);
- usableSegment = ( !_options.sharedRegionEligible() || (strcmp(atom->section().segmentName(), infoB.target->section().segmentName()) == 0) );
isADRP = parseADRP(infoA.instruction, adrpInfoA);
LOH_ASSERT(isADRP);
isADD = parseADD(infoB.instruction, addInfoB);
LOH_ASSERT(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
// 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) ) {
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) ) {
}
}
-
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<std::pair<uint64_t, uint64_t>> 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<uint64_t, uint64_t>(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<uint64_t, uint64_t>(bitcodeSectOffset, bitcodePaddingEnd));
+ }
uint32_t stabsStringsOffsetStart;
uint32_t tabsStringsOffsetEnd;
uint32_t stabsOffsetStart;
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<uint64_t, uint64_t>(firstStabNlistFileOffset, lastStabNlistFileOffset));
+ excludeRegions.emplace_back(std::pair<uint64_t, uint64_t>(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 {
_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)
{
char tmpOutput[PATH_MAX];
uint8_t *wholeBuffer;
if ( outputIsRegularFile && outputIsMappableFile ) {
+ // <rdar://problem/20959031> 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);
if ( ::write(fd, wholeBuffer, _fileSize) == -1 ) {
throwf("can't write to output file: %s, errno=%d", _options.outputFilePath(), errno);
}
+ sDescriptorOfPathToRemove = -1;
::close(fd);
// <rdar://problem/13118223> 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
localRelocationsSection = state.addAtom(*_localRelocsAtom);
}
if ( _hasSplitSegInfo ) {
- _splitSegInfoAtom = new SplitSegInfoAtom<x86>(_options, state, *this);
+ _splitSegInfoAtom = new SplitSegInfoV1Atom<x86>(_options, state, *this);
splitSegInfoSection = state.addAtom(*_splitSegInfoAtom);
}
if ( _hasFunctionStartsInfo ) {
_optimizationHintsAtom = new OptimizationHintsAtom<x86>(_options, state, *this);
optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
}
- if ( _hasDependentDRInfo ) {
- _dependentDRInfoAtom = new DependentDRAtom<x86>(_options, state, *this);
- dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
- }
if ( _hasSymbolTable ) {
_symbolTableAtom = new SymbolTableAtom<x86>(_options, state, *this);
symbolTableSection = state.addAtom(*_symbolTableAtom);
localRelocationsSection = state.addAtom(*_localRelocsAtom);
}
if ( _hasSplitSegInfo ) {
- _splitSegInfoAtom = new SplitSegInfoAtom<x86_64>(_options, state, *this);
+ _splitSegInfoAtom = new SplitSegInfoV1Atom<x86_64>(_options, state, *this);
splitSegInfoSection = state.addAtom(*_splitSegInfoAtom);
}
if ( _hasFunctionStartsInfo ) {
_optimizationHintsAtom = new OptimizationHintsAtom<x86_64>(_options, state, *this);
optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
}
- if ( _hasDependentDRInfo ) {
- _dependentDRInfoAtom = new DependentDRAtom<x86_64>(_options, state, *this);
- dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
- }
if ( _hasSymbolTable ) {
_symbolTableAtom = new SymbolTableAtom<x86_64>(_options, state, *this);
symbolTableSection = state.addAtom(*_symbolTableAtom);
localRelocationsSection = state.addAtom(*_localRelocsAtom);
}
if ( _hasSplitSegInfo ) {
- _splitSegInfoAtom = new SplitSegInfoAtom<arm>(_options, state, *this);
+ if ( _options.sharedRegionEncodingV2() )
+ _splitSegInfoAtom = new SplitSegInfoV2Atom<arm>(_options, state, *this);
+ else
+ _splitSegInfoAtom = new SplitSegInfoV1Atom<arm>(_options, state, *this);
splitSegInfoSection = state.addAtom(*_splitSegInfoAtom);
}
if ( _hasFunctionStartsInfo ) {
_optimizationHintsAtom = new OptimizationHintsAtom<arm>(_options, state, *this);
optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
}
- if ( _hasDependentDRInfo ) {
- _dependentDRInfoAtom = new DependentDRAtom<arm>(_options, state, *this);
- dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
- }
if ( _hasSymbolTable ) {
_symbolTableAtom = new SymbolTableAtom<arm>(_options, state, *this);
symbolTableSection = state.addAtom(*_symbolTableAtom);
localRelocationsSection = state.addAtom(*_localRelocsAtom);
}
if ( _hasSplitSegInfo ) {
- _splitSegInfoAtom = new SplitSegInfoAtom<arm64>(_options, state, *this);
+ if ( _options.sharedRegionEncodingV2() )
+ _splitSegInfoAtom = new SplitSegInfoV2Atom<arm64>(_options, state, *this);
+ else
+ _splitSegInfoAtom = new SplitSegInfoV1Atom<arm64>(_options, state, *this);
splitSegInfoSection = state.addAtom(*_splitSegInfoAtom);
}
if ( _hasFunctionStartsInfo ) {
_optimizationHintsAtom = new OptimizationHintsAtom<arm64>(_options, state, *this);
optimizationHintsSection = state.addAtom(*_optimizationHintsAtom);
}
- if ( _hasDependentDRInfo ) {
- _dependentDRInfoAtom = new DependentDRAtom<arm64>(_options, state, *this);
- dependentDRsSection = state.addAtom(*_dependentDRInfoAtom);
- }
if ( _hasSymbolTable ) {
_symbolTableAtom = new SymbolTableAtom<arm64>(_options, state, *this);
symbolTableSection = state.addAtom(*_symbolTableAtom);
}
-
void OutputFile::makeSplitSegInfo(ld::Internal& state)
{
if ( !_options.sharedRegionEligible() )
}
}
+void OutputFile::makeSplitSegInfoV2(ld::Internal& state)
+{
+ static const bool log = false;
+ if ( !_options.sharedRegionEligible() )
+ return;
+
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit = state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->isSectionHidden() )
+ continue;
+ bool codeSection = (sect->type() == ld::Section::typeCode);
+ if (log) fprintf(stderr, "sect: %s, address=0x%llX\n", sect->sectionName(), sect->address);
+ for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ const ld::Atom* target = NULL;
+ 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)
{
}
}
-
// used to sort atoms with debug notes
class DebugNoteSorter
{
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;
};
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);
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);
const bool _hasSplitSegInfo;
const bool _hasFunctionStartsInfo;
const bool _hasDataInCodeInfo;
- const bool _hasDependentDRInfo;
bool _hasDynamicSymbolTable;
bool _hasLocalRelocations;
bool _hasExternalRelocations;
std::vector<BindingInfo> _lazyBindingInfo;
std::vector<BindingInfo> _weakBindingInfo;
std::vector<SplitSegInfoEntry> _splitSegInfos;
+ std::vector<SplitSegInfoV2Entry> _splitSegV2Infos;
class HeaderAndLoadCommandsAbtract* _headersAndLoadCommandAtom;
class RelocationsAtomAbstract* _sectionsRelocationsAtom;
class RelocationsAtomAbstract* _localRelocsAtom;
class LinkEditAtom* _splitSegInfoAtom;
class LinkEditAtom* _functionStartsAtom;
class LinkEditAtom* _dataInCodeAtom;
- class LinkEditAtom* _dependentDRInfoAtom;
class LinkEditAtom* _optimizationHintsAtom;
};
#include "Options.h"
#include "ld.hpp"
+#include "Bitcode.hpp"
#include "InputFiles.h"
#include "SymbolTable.h"
#include "Resolver.h"
_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 ) {
}
}
-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);
}
}
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:
_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 ) {
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() ) {
}
}
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:
if ( (depInstallName != NULL) && (depInstallName[0] != '/') ) {
if ( (_options.iOSVersionMin() != iOSVersionUnset) && (_options.iOSVersionMin() < iOS_8_0) ) {
// <rdar://problem/17598404> 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());
+ }
}
}
// 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 ) {
// <rdar://problem/5524973> -exported_symbols_list that has wildcards and -dead_strip
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()
this->markLive(**it, &rootChain);
}
+ // special case atoms that need to be live if they reference something live
+ if ( ! _dontDeadStripIfReferencesLive.empty() ) {
+ for (std::vector<const ld::Atom*>::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 ) {
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);
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();
ld::Internal& _internal;
std::vector<const ld::Atom*> _atoms;
std::set<const ld::Atom*> _deadStripRoots;
+ std::vector<const ld::Atom*> _dontDeadStripIfReferencesLive;
std::vector<const ld::Atom*> _atomsWithUnresolvedReferences;
std::vector<const class AliasAtom*> _aliasesFromCmdLine;
SymbolTable _symbolTable;
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");
}
CStringToSlot _cstringTable;
NameToMap _nonStdCStringSectionToMap;
ReferencesToSlot _nonLazyPointerTable;
+ ReferencesToSlot _threadPointerTable;
ReferencesToSlot _cfStringTable;
ReferencesToSlot _objc2ClassRefTable;
ReferencesToSlot _pointerToCStringTable;
//
// Get the local alignment for a type, as used by the acting compiler.
//
-template <class T>
-inline size_t alignof() { struct { char c; T t; } s; return sizeof(s) - sizeof(T); }
+template <typename T>
+unsigned long myalignof() {
+ struct { char c; T t; } s;
+ return sizeof(s) - sizeof(T);
+}
//
#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"
void setSectionSizesAndAlignments();
void sortSections();
void markAtomsOrdered() { _atomsOrderedInSections = true; }
+ bool hasReferenceToWeakExternal(const ld::Atom& atom);
+
virtual ~InternalState() {}
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);
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<const char*> InternalState::FinalSection::_s_segmentsSeen;
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 )
if ( sect.type() == ld::Section::typeLastSection )
return INT_MAX;
const std::vector<const char*>* 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 char*>::const_iterator it=sectionList->begin(); it != sectionList->end(); ++it, ++count) {
if ( strcmp(*it, sect.sectionName()) == 0 )
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;
}
#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;
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);
}
}
}
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);
}
const std::vector<Options::SegmentRename>& segRenames = _options.segmentRenames();
for ( std::vector<Options::SectionRename>::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 ) {
}
}
}
-
-
+
// if no override, use default location
if ( fs == NULL ) {
fs = this->getFinalSection(atom.section());
// normal case
fs->atoms.push_back(&atom);
}
+ this->atomToSection[&atom] = fs;
return fs;
}
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 )
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<ld::Internal::FinalSection*>::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<ld::Internal::FinalSection*>::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());
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<ld::Internal::FinalSection*>::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;
if ( log ) fprintf(stderr, "Regular layout segments:\n");
for (std::vector<ld::Internal::FinalSection*>::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;
lastSegName = sect->segmentName();
}
}
+
// adjust section address based on alignment
uint64_t unalignedAddress = address;
uint64_t alignment = (1 << sect->alignment);
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();
#include <unistd.h>
#include <assert.h>
+#include <set>
+#include <map>
#include <vector>
+#include <string>
#include <unordered_set>
#include "configure.h"
namespace ld {
+// Forward declaration for bitcode support
+class Bitcode;
+
//
// ld::File
//
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;
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
{
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;
virtual bool canScatterAtoms() const = 0;
virtual bool hasLongBranchStubs() { return false; }
virtual LinkerOptionsList* linkerOptions() const = 0;
+ virtual SourceKind sourceKind() const { return kSourceUnknown; }
};
} // namespace relocatable
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<bool, bool> 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;
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,
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,
_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 ) {
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); }
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; }
bool _overridesADylibsWeakDef : 1;
bool _coalescedAway : 1;
bool _live : 1;
+ bool _dontDeadStripIfRefLive : 1;
unsigned _machoSection : 8;
WeakImportState _weakImportState : 2;
};
typedef std::unordered_set<const char*, ld::CStringHash, ld::CStringEquals> CStringSet;
+
class Internal
{
public:
bool hasExternalRelocs;
};
+ typedef std::map<const ld::Atom*, FinalSection*> AtomToSection;
+
virtual uint64_t assignFileOffsets() = 0;
virtual void setSectionSizesAndAlignments() = 0;
virtual ld::Internal::FinalSection* addAtom(const Atom&) = 0;
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<FinalSection*> sections;
std::vector<ld::dylib::File*> dylibs;
std::vector<ld::relocatable::File::Stab> stabs;
+ AtomToSection atomToSection;
CStringSet linkerOptionLibraries;
CStringSet linkerOptionFrameworks;
std::vector<const ld::Atom*> indirectBindingTable;
+ std::vector<const ld::relocatable::File*> filesWithBitcode;
const ld::dylib::File* bundleLoader;
const Atom* entryPoint;
const Atom* classicBindingHelper;
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;
};
-
-
-
-
} // namespace ld
#endif // __LD_HPP__
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __LTO_READER_H__
-#define __LTO_READER_H__
-
-#include <stdlib.h>
-#include <mach-o/dyld.h>
-#include <vector>
-#include <ext/hash_set>
-#include <ext/hash_map>
-
-#include "MachOFileAbstraction.hpp"
-#include "Architectures.hpp"
-#include "ld.hpp"
-
-#include "llvm-c/lto.h"
-
-
-namespace lto {
-
-
-//
-// ld64 only tracks non-internal symbols from an llvm bitcode file.
-// We model this by having an InternalAtom which represent all internal functions and data.
-// All non-interal symbols from a bitcode file are represented by an Atom
-// and each Atom has a reference to the InternalAtom. The InternalAtom
-// also has references to each symbol external to the bitcode file.
-//
-class InternalAtom : public ld::Atom
-{
-public:
- InternalAtom(class File& f);
- // overrides of ld::Atom
- virtual ld::File* file() const { return &_file; }
- virtual bool translationUnitSource(const char** dir, const char** nm) const
- { return false; }
- virtual const char* name() const { return "import-atom"; }
- virtual uint64_t size() const { return 0; }
- virtual uint64_t objectAddress() const { return 0; }
- virtual void copyRawContent(uint8_t buffer[]) const { }
- virtual void setScope(Scope) { }
- virtual ld::Fixup::iterator fixupsBegin() { return &_undefs[0]; }
- virtual ld::Fixup::iterator fixupsEnd() { return &_undefs[_undefs.size()]; }
-
- // for adding references to symbols outside bitcode file
- void addReference(const char* name)
- { _undefs.push_back(ld::Fixup(0, ld::Fixup::k1of1,
- ld::Fixup::fixupNone, false, name)); }
-private:
-
- ld::File& _file;
- std::vector<ld::Fixup> _undefs;
-};
-
-
-//
-// LLVM bitcode file
-//
-class File : public ld::relocatable::File
-{
-public:
- File(const char* path, time_t mTime, const uint8_t* content,
- uint32_t contentLength, uint32_t ordinal, cpu_type_t arch);
- virtual ~File();
-
- // overrides of ld::File
- virtual bool forEachAtom(ld::File::AtomHandler&);
- virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&)
- { return false; }
-
- // overrides of ld::relocatable::File
- virtual bool objcReplacementClasses() { return false; }
- virtual DebugInfoKind debugInfo() { return ld::relocatable::File::kDebugInfoNone; }
- virtual std::vector<ld::relocatable::File::Stab>* stabs() { return NULL; }
- virtual bool canScatterAtoms() { return true; }
-
- lto_module_t module() { return _module; }
- class InternalAtom& internalAtom() { return _internalAtom; }
-private:
- friend class Atom;
- friend class InternalAtom;
-
- cpu_type_t _architecture;
- class InternalAtom _internalAtom;
- class Atom* _atomArray;
- uint32_t _atomArrayCount;
- lto_module_t _module;
- ld::Section _section;
-};
-
-//
-// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially,
-// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After
-// optimization is performed, real Atoms are created for these symobls. However these real Atoms
-// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate
-// methods to real atom.
-//
-class Atom : public ld::Atom
-{
-public:
- Atom(File& f, const char* name, ld::Atom::Scope s,
- ld::Atom::Definition d, ld::Atom::Alignment a);
-
- // overrides of ld::Atom
- virtual ld::File* file() const { return &_file; }
- virtual bool translationUnitSource(const char** dir, const char** nm) const
- { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); }
- virtual const char* name() const { return _name; }
- virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); }
- virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); }
- virtual void copyRawContent(uint8_t buffer[]) const
- { if (_compiledAtom) _compiledAtom->copyRawContent(buffer); }
-
- ld::Atom* compiledAtom() { return _compiledAtom; }
- void setCompiledAtom(ld::Atom& atom)
- { _compiledAtom = &atom; }
-private:
-
- File& _file;
- const char* _name;
- ld::Atom* _compiledAtom;
-};
-
-
-
-
-
-
-
-class Parser
-{
-public:
- static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture);
- static const char* fileKind(const uint8_t* fileContent);
- static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- time_t modTime, uint32_t ordinal, cpu_type_t architecture);
- static bool libLTOisLoaded() { return (::lto_get_version() != NULL); }
- static bool optimize(const std::vector<ld::Atom*>& allAtoms, std::vector<ld::Atom*>& newAtoms,
- std::vector<const char*>& additionalUndefines,
- const std::set<ld::Atom*>&,
- std::vector<ld::Atom*>& newDeadAtoms,
- uint32_t nextInputOrdinal,
- ld::OutFile* writer, ld::Atom* entryPointAtom,
- const std::vector<const char*>& llvmOptions,
- bool allGlobalsAReDeadStripRoots,
- bool verbose, bool saveTemps,
- const char* outputFilePath,
- bool pie, bool mainExecutable, bool staticExecutable, bool relocatable,
- bool allowTextRelocs, cpu_type_t arch);
-
- static const char* ltoVersion() { return ::lto_get_version(); }
-
-private:
- static const char* tripletPrefixForArch(cpu_type_t arch);
- static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch);
-
- class CStringEquals
- {
- public:
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
- typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet;
- typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom;
-
- class AtomSyncer : public ld::File::AtomHandler {
- public:
- AtomSyncer(std::vector<const char*>& a, std::vector<ld::Atom*>&na,
- CStringToAtom la, CStringToAtom dla) :
- additionalUndefines(a), newAtoms(na), llvmAtoms(la), deadllvmAtoms(dla) { }
- virtual void doAtom(class ld::Atom&);
-
- std::vector<const char*>& additionalUndefines;
- std::vector<ld::Atom*>& newAtoms;
- CStringToAtom llvmAtoms;
- CStringToAtom deadllvmAtoms;
- };
-
- static std::vector<File*> _s_files;
-};
-
-std::vector<File*> Parser::_s_files;
-
-
-const char* Parser::tripletPrefixForArch(cpu_type_t arch)
-{
- switch (arch) {
- case CPU_TYPE_POWERPC:
- return "powerpc-";
- case CPU_TYPE_POWERPC64:
- return "powerpc64-";
- case CPU_TYPE_I386:
- return "i386-";
- case CPU_TYPE_X86_64:
- return "x86_64-";
- case CPU_TYPE_ARM:
- return "arm";
- }
- return "";
-}
-
-bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture)
-{
- return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture));
-}
-
-const char* Parser::fileKind(const uint8_t* p)
-{
- if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) {
- uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16])));
- switch (arch) {
- case CPU_TYPE_POWERPC:
- return "ppc";
- case CPU_TYPE_I386:
- return "i386";
- case CPU_TYPE_X86_64:
- return "x86_64";
- case CPU_TYPE_ARM:
- return "arm";
- }
- return "unknown bitcode architecture";
- }
- return NULL;
-}
-
-File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime,
- uint32_t ordinal, cpu_type_t architecture)
-{
- File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture);
- _s_files.push_back(f);
- return f;
-}
-
-
-ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, cpu_type_t arch)
-{
- switch ( arch ) {
- case CPU_TYPE_POWERPC:
- if ( mach_o::relocatable::Parser<ppc>::validFile(p) )
- return mach_o::relocatable::Parser<ppc>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal);
- break;
- case CPU_TYPE_POWERPC64:
- if ( mach_o::relocatable::Parser<ppc64>::validFile(p) )
- return mach_o::relocatable::Parser<ppc64>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal);
- break;
- case CPU_TYPE_I386:
- if ( mach_o::relocatable::Parser<x86>::validFile(p) )
- return mach_o::relocatable::Parser<x86>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal);
- break;
- case CPU_TYPE_X86_64:
- if ( mach_o::relocatable::Parser<x86_64>::validFile(p) )
- return mach_o::relocatable::Parser<x86_64>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal);
- break;
- case CPU_TYPE_ARM:
- if ( mach_o::relocatable::Parser<arm>::validFile(p) )
- return mach_o::relocatable::Parser<arm>::parse(p, len, "/tmp/lto.o", 0, nextInputOrdinal);
- break;
- }
- throw "LLVM LTO, file is not of required architecture";
-}
-
-
-
-File::File(const char* path, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ordinal, cpu_type_t arch)
- : ld::relocatable::File(path,mTime,ordinal), _architecture(arch), _internalAtom(*this),
- _atomArray(NULL), _atomArrayCount(0), _module(NULL),
- _section("__TEXT_", "__tmp_lto", ld::Section::typeUnclassified)
-{
- // create llvm module
- _module = ::lto_module_create_from_memory(content, contentLength);
- if ( _module == NULL )
- throwf("could not parse object file %s: %s", path, lto_get_error_message());
-
- // create atom for each global symbol in module
- uint32_t count = ::lto_module_get_num_symbols(_module);
- _atomArray = (Atom*)malloc(sizeof(Atom)*count);
- for (uint32_t i=0; i < count; ++i) {
- const char* name = ::lto_module_get_symbol_name(_module, i);
- lto_symbol_attributes attr = lto_module_get_symbol_attribute(_module, i);
-
- // <rdar://problem/6378110> LTO doesn't like dtrace symbols
- // ignore dtrace static probes for now
- // later when codegen is done and a mach-o file is produces the probes will be processed
- if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) )
- continue;
-
- ld::Atom::Definition def;
- switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) {
- case LTO_SYMBOL_DEFINITION_REGULAR:
- def = ld::Atom::definitionRegular;
- break;
- case LTO_SYMBOL_DEFINITION_TENTATIVE:
- def = ld::Atom::definitionTentative;
- break;
- case LTO_SYMBOL_DEFINITION_WEAK:
- def = ld::Atom::definitionRegular;
- break;
- case LTO_SYMBOL_DEFINITION_UNDEFINED:
- case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
- def = ld::Atom::definitionProxy;
- break;
- default:
- throwf("unknown definition kind for symbol %s in bitcode file %s", name, path);
- }
-
- // make LLVM atoms for definitions and a reference for undefines
- if ( def != ld::Atom::definitionProxy ) {
- ld::Atom::Scope scope;
- switch ( attr & LTO_SYMBOL_SCOPE_MASK) {
- case LTO_SYMBOL_SCOPE_INTERNAL:
- scope = ld::Atom::scopeTranslationUnit;
- break;
- case LTO_SYMBOL_SCOPE_HIDDEN:
- scope = ld::Atom::scopeLinkageUnit;
- break;
- case LTO_SYMBOL_SCOPE_DEFAULT:
- scope = ld::Atom::scopeGlobal;
- break;
- default:
- throwf("unknown scope for symbol %s in bitcode file %s", name, path);
- }
- // only make atoms for non-internal symbols
- if ( scope == ld::Atom::scopeTranslationUnit )
- continue;
- uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK);
- // make Atom using placement new operator
- new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, alignment);
- }
- else {
- // add to list of external references
- _internalAtom.addReference(name);
- }
- }
-}
-
-File::~File()
-{
- if ( _module != NULL )
- ::lto_module_dispose(_module);
-}
-
-bool File::forEachAtom(ld::File::AtomHandler& handler)
-{
- handler.doAtom(_internalAtom);
- for(uint32_t i=0; i < _atomArrayCount; ++i) {
- handler.doAtom(_atomArray[i]);
- }
- return true;
-}
-
-InternalAtom::InternalAtom(File& f)
- : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit,
- ld::Atom::typeLTOtemporary, ld::Atom::symbolTableNotIn, false, false, ld::Atom::Alignment(0)),
- _file(f)
-{
-}
-
-Atom::Atom(File& f, const char* name, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Alignment a)
- : ld::Atom(f._section, d, ld::Atom::combineNever, s, ld::Atom::typeLTOtemporary, ld::Atom::symbolTableIn, false, false, a),
- _file(f), _name(name), _compiledAtom(NULL)
-{
-}
-
-
-
-
-bool Parser::optimize(const std::vector<ld::Atom*>& allAtoms, std::vector<ld::Atom*>& newAtoms,
- std::vector<const char*>& additionalUndefines,
- const std::set<ld::Atom*>& deadAtoms,
- std::vector<ld::Atom*>& newlyDeadAtoms,
- uint32_t nextInputOrdinal,
- ld::OutFile* writer, ld::Atom* entryPointAtom,
- const std::vector<const char*>& llvmOptions,
- bool allGlobalsAReDeadStripRoots,
- bool verbose, bool saveTemps,
- const char* outputFilePath,
- bool pie, bool mainExecutable, bool staticExecutable, bool relocatable,
- bool allowTextRelocs, cpu_type_t arch)
-{
- // exit quickly if nothing to do
- if ( _s_files.size() == 0 )
- return false;
-
- // print out LTO version string if -v was used
- if ( verbose )
- fprintf(stderr, "%s\n", lto_get_version());
-
- // create optimizer and add each Reader
- lto_code_gen_t generator = ::lto_codegen_create();
- for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) {
- if ( ::lto_codegen_add_module(generator, (*it)->module()) )
- throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message());
- }
-
- // add any -mllvm command line options
- for (std::vector<const char*>::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) {
- ::lto_codegen_debug_options(generator, *it);
- }
-
- // The atom graph uses directed edges (references). Collect all references where
- // originating atom is not part of any LTO Reader. This allows optimizer to optimize an
- // external (i.e. not originated from same .o file) reference if all originating atoms are also
- // defined in llvm bitcode file.
- CStringSet nonLLVMRefs;
- CStringToAtom llvmAtoms;
- bool hasNonllvmAtoms = false;
- for (std::vector<ld::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
- ld::Atom* atom = *it;
- // only look at references that come from an atom that is not an llvm atom
- if ( atom->contentType() != ld::Atom::typeLTOtemporary ) {
- // remember if we've seen any atoms not from an llvm reader and not from the writer
-// if ( atom->getFile() != writer )
-// hasNonllvmAtoms = true;
- for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
- if ( fit->binding != ld::Fixup::bindingByNameBound )
- continue;
- // and reference an llvm atom
- if ( fit->u.target->contentType() == ld::Atom::typeLTOtemporary )
- nonLLVMRefs.insert(fit->u.target->name());
- }
- }
- else {
- llvmAtoms[atom->name()] = (Atom*)atom;
- }
- }
- // if entry point is in a llvm bitcode file, it must be preserved by LTO
- if ( entryPointAtom != NULL ) {
- if ( entryPointAtom->contentType() == ld::Atom::typeLTOtemporary )
- nonLLVMRefs.insert(entryPointAtom->name());
- }
-
- // deadAtoms are the atoms that the linker coalesced. For instance weak or tentative definitions
- // overriden by another atom. If any of these deadAtoms are llvm atoms and they were replaced
- // with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead
- // atom so that the linker can replace it with the mach-o one later.
- CStringToAtom deadllvmAtoms;
- for (std::set<ld::Atom*>::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) {
- ld::Atom* atom = *it;
- if ( atom->contentType() == ld::Atom::typeLTOtemporary ) {
- const char* name = atom->name();
- ::lto_codegen_add_must_preserve_symbol(generator, name);
- deadllvmAtoms[name] = (Atom*)atom;
- }
- }
-
-
- // tell code generator about symbols that must be preserved
- for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) {
- const char* name = it->first;
- Atom* atom = it->second;
- // Include llvm Symbol in export list if it meets one of following two conditions
- // 1 - atom scope is global (and not linkage unit).
- // 2 - included in nonLLVMRefs set.
- // If a symbol is not listed in exportList then LTO is free to optimize it away.
- if ( (atom->scope() == ld::Atom::scopeGlobal) )
- ::lto_codegen_add_must_preserve_symbol(generator, name);
- else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() )
- ::lto_codegen_add_must_preserve_symbol(generator, name);
- }
-
- // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o)
- if ( relocatable && !hasNonllvmAtoms ) {
- if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) {
- // HACK, no good way to tell linker we are all done, so just quit
- exit(0);
- }
- warning("could not produce merged bitcode file");
- }
-
- // set code-gen model
- lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
- if ( mainExecutable ) {
- if ( staticExecutable ) {
- // darwin x86_64 "static" code model is really dynamic code model
- if ( arch == CPU_TYPE_X86_64 )
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
- else
- model = LTO_CODEGEN_PIC_MODEL_STATIC;
- }
- else {
- if ( pie )
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
- else
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
- }
- }
- else {
- if ( allowTextRelocs )
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
- else
- model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
- }
- if ( ::lto_codegen_set_pic_model(generator, model) )
- throwf("could not create set codegen model: %s", lto_get_error_message());
-
- // if requested, save off merged bitcode file
- if ( saveTemps ) {
- char tempBitcodePath[MAXPATHLEN];
- strcpy(tempBitcodePath, outputFilePath);
- strcat(tempBitcodePath, ".lto.bc");
- ::lto_codegen_write_merged_modules(generator, tempBitcodePath);
- }
-
-#if LTO_API_VERSION >= 3
- // find assembler next to linker
- char path[PATH_MAX];
- uint32_t bufSize = PATH_MAX;
- if ( _NSGetExecutablePath(path, &bufSize) != -1 ) {
- char* lastSlash = strrchr(path, '/');
- if ( lastSlash != NULL ) {
- strcpy(lastSlash+1, "as");
- struct stat statInfo;
- if ( stat(path, &statInfo) == 0 )
- ::lto_codegen_set_assembler_path(generator, path);
- }
- }
-#endif
- // run code generator
- size_t machOFileLen;
- const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen);
- if ( machOFile == NULL )
- throwf("could not do LTO codegen: %s", ::lto_get_error_message());
-
- // if requested, save off temp mach-o file
- if ( saveTemps ) {
- char tempMachoPath[MAXPATHLEN];
- strcpy(tempMachoPath, outputFilePath);
- strcat(tempMachoPath, ".lto.o");
- int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
- if ( fd != -1) {
- ::write(fd, machOFile, machOFileLen);
- ::close(fd);
- }
- // save off merged bitcode file
- char tempOptBitcodePath[MAXPATHLEN];
- strcpy(tempOptBitcodePath, outputFilePath);
- strcat(tempOptBitcodePath, ".lto.opt.bc");
- ::lto_codegen_write_merged_modules(generator, tempOptBitcodePath);
- }
-
- // parse generated mach-o file into a MachOReader
- ld::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, arch);
-
- // sync generated mach-o atoms with existing atoms ld knows about
- AtomSyncer syncer(additionalUndefines,newAtoms,llvmAtoms,deadllvmAtoms);
- machoFile->forEachAtom(syncer);
-
- // Remove InternalAtoms from ld
- for (std::vector<File*>::iterator it=_s_files.begin(); it != _s_files.end(); ++it) {
- newlyDeadAtoms.push_back(&((*it)->internalAtom()));
- }
- // Remove Atoms from ld if code generator optimized them away
- for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) {
- // check if setRealAtom() called on this Atom
- if ( li->second->compiledAtom() == NULL )
- newlyDeadAtoms.push_back(li->second);
- }
-
- return true;
-}
-
-
-void Parser::AtomSyncer::doAtom(ld::Atom& machoAtom)
-{
- // update proxy atoms to point to real atoms and find new atoms
- const char* name = machoAtom.name();
- if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) {
- CStringToAtom::iterator pos = llvmAtoms.find(name);
- if ( pos != llvmAtoms.end() ) {
- // turn Atom into a proxy for this mach-o atom
- pos->second->setCompiledAtom(machoAtom);
- }
- else {
- // an atom of this name was not in the allAtoms list the linker gave us
- if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) {
- // this corresponding to an atom that the linker coalesced away.
- // Don't pass it back as a new atom
- }
- else
- {
- // this is something new that lto conjured up, tell ld its new
- newAtoms.push_back(&machoAtom);
- }
- }
- }
- else {
- // ld only knew about non-satic atoms, so this one must be new
- newAtoms.push_back(&machoAtom);
- }
-
- // adjust fixups to go through proxy atoms
- for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(); fit != machoAtom.fixupsEnd(); ++fit) {
- switch ( fit->binding ) {
- case ld::Fixup::bindingNone:
- break;
- case ld::Fixup::bindingByNameUnbound:
- // don't know if this target has been seen by linker before or if it is new
- // be conservitive and tell linker it is new
- additionalUndefines.push_back(fit->u.name);
- break;
- case ld::Fixup::bindingByNameBound:
- break;
- case ld::Fixup::bindingDirectlyBound:
- // If mach-o atom is referencing another mach-o atom then
- // reference is not going through Atom proxy. Fix it here to ensure that all
- // llvm symbol references always go through Atom proxy.
- break;
- case ld::Fixup::bindingByContentBound:
- break;
- }
- }
-
-}
-
-
-
-}; // namespace lto
-
-
-#endif
-
#include <pthread.h>
#include <mach-o/dyld.h>
#include <vector>
+#include <map>
#include <unordered_set>
#include <unordered_map>
_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;
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;
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";
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)
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 )
if ( log ) fprintf(stderr, "\t%s (undefined)\n", name);
}
}
+
+#if LTO_API_VERSION >= 11
+ if ( sSupportsLocalContext )
+ this->release();
+#endif
}
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;
+
+ // <rdar://problem/15471128> linker should release module as soon as possible
+ this->release();
+ return false;
+}
+
void File::release()
{
if ( _module != NULL )
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:
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
// <rdar://problem/12379604> 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<File*>::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());
- // <rdar://problem/15471128> linker should release module as soon as possible
- f->release();
lastOrdinal = f->ordinal();
}
// 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);
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);
}
}
}
#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 ) {
::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
}
else {
// <rdar://problem/12859831> 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;
~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
}
+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
//
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);
}
//
bool preserveAllGlobals;
bool verbose;
bool saveTemps;
+ bool ltoCodegenOnly;
bool pie;
bool mainExecutable;
bool staticExecutable;
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<const char*>* llvmOptions;
const std::vector<const char*>* initialUndefines;
};
-
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
* Copyright (c) 2005-2011 Apple Inc. All rights reserved.
#include <vector>
#include <set>
+#include <map>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include "Architectures.hpp"
+#include "Bitcode.hpp"
#include "MachOFileAbstraction.hpp"
#include "MachOTrie.hpp"
#include "macho_dylib_file.h"
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
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; }
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<A>* file; };
-
- void assertNoReExportCycles(ReExportChain*);
+ virtual void assertNoReExportCycles(ReExportChain*) const;
private:
typedef typename A::P P;
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<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals> NameToAtomMap;
typedef std::unordered_set<const char*, CStringHash, ld::CStringEquals> NameSet;
struct Dependent { const char* path; File<A>* dylib; bool reExport; };
- bool containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const;
+ virtual std::pair<bool, bool> 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);
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;
NameSet _ignoreExports;
const char* _parentUmbrella;
ImportAtom<A>* _importAtom;
- const void* _codeSignatureDR;
bool _noRexports;
bool _hasWeakExports;
bool _deadStrippable;
bool _installPathOverride;
bool _indirectDylibsProcessed;
bool _appExtensionSafe;
- ld::MacVersionMin _macMinVersionInDylib;
- ld::IOSVersionMin _iOSMinVersionInDylib;
+ uint32_t _minVersionInDylib;
+ uint32_t _platformInDylib;
+ std::unique_ptr<ld::Bitcode> _bitcode;
static bool _s_logHashtable;
};
template <typename A>
File<A>::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<P>* header = (const macho_header<P>*)fileContent;
const uint32_t cmd_count = header->ncmds();
// pass 1: get pointers, and see if this dylib uses compressed LINKEDIT format
const macho_dysymtab_command<P>* dynamicInfo = NULL;
const macho_dyld_info_command<P>* dyldInfo = NULL;
- const macho_linkedit_data_command<P>* codeSignature = NULL;
const macho_nlist<P>* symbolTable = NULL;
const char* strings = NULL;
bool compressedLinkEdit = false;
uint32_t dependentLibCount = 0;
+ Options::Platform lcPlatform = Options::kPlatformUnknown;
const macho_load_command<P>* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
macho_dylib_command<P>* dylibID;
break;
case LC_SUB_CLIENT:
_allowableClients.push_back(strdup(((macho_sub_client_command<P>*)cmd)->client()));
+ // <rdar://problem/20627554> 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<P>*)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<P>*)cmd)->version();
+ case LC_VERSION_MIN_WATCHOS:
+ #if SUPPORT_APPLE_TV
+ case LC_VERSION_MIN_TVOS:
+ #endif
+ _minVersionInDylib = (ld::MacVersionMin)((macho_version_min_command<P>*)cmd)->version();
+ _platformInDylib = cmd->cmd();
+ lcPlatform = Options::platformForLoadCommand(_platformInDylib);
break;
case LC_CODE_SIGNATURE:
- codeSignature = (macho_linkedit_data_command<P>* )cmd;
break;
case macho_segment_command<P>::CMD:
// check for Objective-C info
- if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), objCInfoSegmentName()) == 0 ) {
+ if ( strncmp(((macho_segment_command<P>*)cmd)->segname(), objCInfoSegmentName(), 6) == 0 ) {
const macho_segment_command<P>* segment = (macho_segment_command<P>*)cmd;
const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segment + sizeof(macho_segment_command<P>));
const macho_section<P>* const sectionsEnd = §ionsStart[segment->nsects()];
}
}
}
+ // 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<P>*)cmd)->segname(), "__LLVM") == 0 ) {
+ const macho_section<P>* const sect = (macho_section<P>*)((char*)cmd + sizeof(macho_segment_command<P>));
+ if ( strncmp(sect->sectname(), "__bundle", 8) == 0 )
+ _bitcode = std::unique_ptr<ld::Bitcode>(new ld::Bitcode(NULL, sect->size()));
+ }
}
cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
if ( cmd > cmdsEnd )
throwf("malformed dylb, load command #%d is outside size of load commands in %s", i, pth);
}
+ // arm/arm64 objects are default to ios platform if not set.
+ // rdar://problem/21746314
+ if (lcPlatform == Options::kPlatformUnknown &&
+ (std::is_same<A, arm>::value || std::is_same<A, arm64>::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
}
// 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;
}
_importAtom = new ImportAtom<A>(*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<Security::kSecCodeMagicEmbeddedSignature> EmbeddedSignatureBlob;
- typedef Security::SuperBlob<Security::kSecCodeMagicRequirementSet> InternalRequirementsBlob;
- const EmbeddedSignatureBlob* signature = EmbeddedSignatureBlob::specific(overallSignature);
- if ( signature->validateBlob(codeSignature->datasize()) ) {
- const InternalRequirementsBlob* ireq = signature->find<InternalRequirementsBlob>(Security::cdRequirementsSlot);
- if ( (ireq != NULL) && ireq->validateBlob() ) {
- const Security::BlobCore* dr = ireq->find(Security::kSecDesignatedRequirementType);
- if ( (dr != NULL) && dr->validateBlob(Security::kSecCodeMagicRequirement) ) {
- // <rdar://problem/10968461> 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);
munmap((caddr_t)fileContent, fileLength);
}
+
//
// Parses number of form X[.Y[.Z]] into a uint32_t where the nibbles are xxxx.yy.zz
//
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 ) {
return false;
}
+
+template <typename A>
+std::pair<bool, bool> File<A>::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 <typename A>
bool File<A>::hasWeakDefinition(const char* name) const
{
// if supposed to ignore this export, then pretend I don't have it
if ( _ignoreExports.count(name) != 0 )
return false;
-
- typename NameToAtomMap::const_iterator pos = _atoms.find(name);
- if ( pos != _atoms.end() ) {
- return pos->second.weakDef;
- }
- else {
- // look in children that I re-export
- for (typename std::vector<Dependent>::const_iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); ++it) {
- if ( it->reExport ) {
- //fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->path(), (*it)->getInstallPath());
- typename NameToAtomMap::iterator cpos = it->dylib->_atoms.find(name);
- if ( cpos != it->dylib->_atoms.end() )
- return cpos->second.weakDef;
- }
- }
- }
- return false;
+
+ return hasWeakDefinitionImpl(name).second;
}
template <typename A>
-bool File<A>::containsOrReExports(const char* name, bool* weakDef, bool* tlv, pint_t* defAddress) const
+bool File<A>::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<Dependent>::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;
}
}
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<A>(*this, name, bucket.weakDef, bucket.tlv, bucket.address);
_atoms[name] = bucket;
_providedAtom = true;
}
template <typename A>
-void File<A>::assertNoReExportCycles(ReExportChain* prev)
+void File<A>::assertNoReExportCycles(ReExportChain* prev) const
{
// recursively check my re-exported dylibs
ReExportChain chain;
chain.prev = prev;
chain.file = this;
- for (typename std::vector<Dependent>::iterator it = _dependentDylibs.begin(); it != _dependentDylibs.end(); it++) {
- if ( it->reExport ) {
- ld::File* child = it->dylib;
+ 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);
}
}
}
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);
}
};
#include <set>
#include <map>
#include <algorithm>
+#include <type_traits>
#include "dwarf2.h"
#include "debugline.h"
#include "Architectures.hpp"
+#include "Bitcode.hpp"
#include "ld.hpp"
#include "macho_relocatable_file.h"
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),
_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; }
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:
ld::File::ObjcConstraint _objConstraint;
uint8_t _swiftVersion;
uint32_t _cpuSubType;
+ uint32_t _minOSVersion;
+ uint32_t _platform;
bool _canScatterAtoms;
std::vector<std::vector<const char*> > _linkerOptions;
+ std::unique_ptr<ld::Bitcode> _bitcode;
+ SourceKind _srcKind;
};
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<A>* findAtomByAddress(pint_t addr) { return this->findContentAtomByAddress(addr, this->_beginAtoms, this->_endAtoms); }
virtual bool addFollowOnFixups() const { return ! _file.canScatterAtoms(); }
virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer,
static ld::Fixup::Kind fixupKind();
};
+template <typename A>
+class TLVPointerSection : public FixedSizeSection<A>
+{
+public:
+ TLVPointerSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s)
+ : FixedSizeSection<A>(parser, f, s) {}
+protected:
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::P P;
+
+ virtual ld::Atom::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<A>&, pint_t) { return "tlv_lazy_ptr"; }
+ virtual pint_t elementSizeAtAddress(pint_t addr) { return sizeof(pint_t); }
+ virtual ld::Atom::Combine combine(Parser<A>&, pint_t);
+ virtual bool ignoreLabel(const char* label) const { return true; }
+ virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const;
+ virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs,
+ const ld::IndirectBindingTable& ind) const;
+
+private:
+ static const char* targetName(const class Atom<A>* atom, const ld::IndirectBindingTable& ind, bool* isStatic);
+};
+
template <typename A>
class CFStringSection : public FixedSizeSection<A>
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()),
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:
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<typename A::P>* header);
static bool hasObjC2Categories(const uint8_t* fileContent);
static bool hasObjC1Categories(const uint8_t* fileContent);
+ static bool getNonLocalSymbols(const uint8_t* fileContnet, std::vector<const char*> &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);
}
sectionTypeUTF16Strings, sectionTypeCFString, sectionTypeObjC2ClassRefs, typeObjC2CategoryList,
sectionTypeObjC1Classes, sectionTypeSymboled, sectionTypeObjC1ClassRefs,
sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs,
- sectionTypeCompactUnwind };
+ sectionTypeCompactUnwind, sectionTypeTLVPointers};
template <typename P>
struct MachOSectionAndSectionClass
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[]);
bool _neverConvertDwarf;
bool _verboseOptimizationHints;
bool _armUsesZeroCostExceptions;
+ bool _ignoreMismatchPlatform;
unsigned int _stubsSectionNum;
const macho_section<P>* _stubsMachOSection;
std::vector<const char*> _dtraceProviderInfo;
template <typename A>
Parser<A>::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),
_keepDwarfUnwind(keepDwarfUnwind), _forceDwarfConversion(forceDwarfConversion),
_neverConvertDwarf(neverConvertDwarf),
_verboseOptimizationHints(verboseOptimizationHints),
+ _ignoreMismatchPlatform(ignoreMismatchPlatform),
_stubsSectionNum(0), _stubsMachOSection(NULL)
{
}
const char* Parser<x86_64>::fileKind(const uint8_t* fileContent)
{
const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
+ if ( header->magic() != MH_MAGIC_64 )
return NULL;
if ( header->cputype() != CPU_TYPE_X86_64 )
return NULL;
const char* Parser<arm64>::fileKind(const uint8_t* fileContent)
{
const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
+ if ( header->magic() != MH_MAGIC_64 )
return NULL;
if ( header->cputype() != CPU_TYPE_ARM64 )
return NULL;
return false;
}
+
+template <typename A>
+bool Parser<A>::getNonLocalSymbols(const uint8_t* fileContent, std::vector<const char*> &syms)
+{
+ const macho_header<P>* header = (const macho_header<P>*)fileContent;
+ const uint32_t cmd_count = header->ncmds();
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>));
+ const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == LC_SYMTAB ) {
+ const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
+ uint32_t symbolCount = symtab->nsyms();
+ const macho_nlist<P>* symbols = (const macho_nlist<P>*)(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<P>*)(((char*)cmd)+cmd->cmdsize());
+ if ( cmd > cmdsEnd )
+ throwf("malformed mach-o file, load command #%d is outside size of load commands", i);
+ }
+ return false;
+}
+
+
template <typename A>
int Parser<A>::pointerSorter(const void* l, const void* r)
{
// create file object
_file = new File<A>(_path, _modTime, _fileContent, _ordinal);
+ // set input source
+ _file->setSourceKind(opts.srcKind);
+
// respond to -t option
if ( opts.logAllFiles )
printf("%s\n", _path);
_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
p += sizeof(Atom<A>);
}
assert(fixupOffset == _allFixups.size());
- _file->_fixups.reserve(fixupOffset);
+ _file->_fixups.resize(fixupOffset);
// copy each fixup for each atom
for(typename std::vector<FixupInAtom>::iterator it=_allFixups.begin(); it != _allFixups.end(); ++it) {
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<x86>::loadCommandSizeMask() { return 0x03; }
template <> uint8_t Parser<x86_64>::loadCommandSizeMask() { return 0x07; }
template <> uint8_t Parser<arm64>::loadCommandSizeMask() { return 0x07; }
template <typename A>
-bool Parser<A>::parseLoadCommands()
+bool Parser<A>::parseLoadCommands(Options::Platform platform, uint32_t linkMinOSVersion, bool simulator, bool ignoreMismatchPlatform)
{
const macho_header<P>* header = (const macho_header<P>*)_fileContent;
// <rdar://problem/5394172> 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<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>));
const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
const macho_load_command<P>* cmd = cmds;
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<P>*)cmd)->version();
+ break;
default:
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
if ( segment != NULL )
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<A, arm>::value || std::is_same<A, arm64>::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 )
return true;
}
+template <typename A>
+Options::Platform Parser<A>::findPlatform(const macho_header<P>* header)
+{
+ const uint32_t cmd_count = header->ncmds();
+ if ( cmd_count == 0 )
+ return Options::kPlatformUnknown;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>));
+ const macho_load_command<P>* const cmdsEnd = (macho_load_command<P>*)((char*)header + sizeof(macho_header<P>) + header->sizeofcmds());
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ uint32_t size = cmd->cmdsize();
+ if ( (size & 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<P>*)(((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 <typename A>
void Parser<A>::prescanSymbolTable()
// allocate raw storage for all section objects on stack
MachOSectionAndSectionClass<P>* machOSects = (MachOSectionAndSectionClass<P>*)machOSectsStorage;
unsigned int count = 0;
+ // local variable for bitcode parsing
+ const macho_section<P>* bitcodeSect = NULL;
+ const macho_section<P>* cmdlineSect = NULL;
+ const macho_section<P>* swiftCmdlineSect = NULL;
+ const macho_section<P>* bundleSect = NULL;
+ bool bitcodeAsm = false;
+
for (uint32_t i=0; i < _machOSectionsCount; ++i) {
const macho_section<P>* sect = &_sectionsStart[i];
if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) {
}
}
}
+ 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;
totalSectionsSize += sizeof(NonLazyPointerSection<A>);
machOSects[count++].type = sectionTypeNonLazy;
break;
+ case S_THREAD_LOCAL_VARIABLE_POINTERS:
+ totalSectionsSize += sizeof(TLVPointerSection<A>);
+ machOSects[count++].type = sectionTypeTLVPointers;
+ break;
case S_LITERAL_POINTERS:
if ( (strcmp(sect->segname(), "__OBJC") == 0) && (strcmp(sect->sectname(), "__cls_refs") == 0) ) {
totalSectionsSize += sizeof(Objc1ClassReferences<A>);
totalSectionsSize += sizeof(TLVDefsSection<A>);
machOSects[count++].type = sectionTypeTLVDefs;
break;
- case S_THREAD_LOCAL_VARIABLE_POINTERS:
default:
throwf("unknown section type %d", sect->flags() & SECTION_TYPE);
}
}
+
+ // Create bitcode
+ if ( bitcodeSect != NULL ) {
+ if ( cmdlineSect != NULL )
+ _file->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::ClangBitcode(&_fileContent[bitcodeSect->offset()], bitcodeSect->size(),
+ &_fileContent[cmdlineSect->offset()], cmdlineSect->size()));
+ else if ( swiftCmdlineSect != NULL )
+ _file->_bitcode = std::unique_ptr<ld::Bitcode>(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<ld::Bitcode>(new ld::BundleBitcode(&_fileContent[bundleSect->offset()], bundleSect->size()));
+ else if ( bitcodeAsm )
+ _file->_bitcode = std::unique_ptr<ld::Bitcode>(new ld::AsmBitcode(_fileContent, _fileLength));
// sort by address (mach-o object files don't aways have sections sorted)
::qsort(machOSects, count, sizeof(MachOSectionAndSectionClass<P>), MachOSectionAndSectionClass<P>::sorter);
*objects++ = new (space) NonLazyPointerSection<A>(*this, *_file, machOSects[i].sect);
space += sizeof(NonLazyPointerSection<A>);
break;
+ case sectionTypeTLVPointers:
+ *objects++ = new (space) TLVPointerSection<A>(*this, *_file, machOSects[i].sect);
+ space += sizeof(TLVPointerSection<A>);
+ break;
case sectionTypeCFI:
_EHFrameSection = new (space) CFISection<A>(*this, *_file, machOSects[i].sect);
*objects++ = _EHFrameSection;
p += sizeof(Atom<A>);
}
assert(liOffset == entries.size());
- _file->_lineInfos.reserve(liOffset);
+ _file->_lineInfos.resize(liOffset);
// copy each line info for each atom
for (typename std::vector<AtomAndLineInfo<A> >::iterator it = entries.begin(); it != entries.end(); ++it) {
return _dwarfTranslationUnitPath;
}
-
-
template <typename A>
bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
{
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;
}
const pint_t* content = (pint_t*)(this->file().fileContent() + this->_machOSection->offset() + reloc->r_address());
pint_t personalityAddr = *content;
Section<arm64>* 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);
return ld::Atom::scopeLinkageUnit;
}
+
+
+template <typename A>
+ld::Atom::Combine TLVPointerSection<A>::combine(Parser<A>& parser, pint_t addr)
+{
+ return ld::Atom::combineByNameAndReferences;
+}
+
+
+template <typename A>
+const char* TLVPointerSection<A>::targetName(const class Atom<A>* 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 <typename A>
+unsigned long TLVPointerSection<A>::contentHash(const class Atom<A>* 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 <typename A>
+bool TLVPointerSection<A>::canCoalesceWith(const class Atom<A>* 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<A>* rhsAtom = dynamic_cast<const Atom<A>*>(&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 <typename A>
const uint8_t* CFStringSection<A>::targetContent(const class Atom<A>* atom, const ld::IndirectBindingTable& ind,
ContentType* ct, unsigned int* count)
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:
//
// 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<x86_64>::validFile(fileContent) ) {
*result = CPU_TYPE_X86_64;
const macho_header<Pointer64<LittleEndian> >* header = (const macho_header<Pointer64<LittleEndian> >*)fileContent;
*subResult = header->cpusubtype();
+ *platform = Parser<x86_64>::findPlatform(header);
return true;
}
if ( mach_o::relocatable::Parser<x86>::validFile(fileContent) ) {
+ const macho_header<Pointer32<LittleEndian> >* header = (const macho_header<Pointer32<LittleEndian> >*)fileContent;
*result = CPU_TYPE_I386;
*subResult = CPU_SUBTYPE_X86_ALL;
+ *platform = Parser<x86>::findPlatform(header);
return true;
}
if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, false, 0) ) {
- *result = CPU_TYPE_ARM;
const macho_header<Pointer32<LittleEndian> >* header = (const macho_header<Pointer32<LittleEndian> >*)fileContent;
+ *result = CPU_TYPE_ARM;
*subResult = header->cpusubtype();
+ *platform = Parser<arm>::findPlatform(header);
return true;
}
if ( mach_o::relocatable::Parser<arm64>::validFile(fileContent, false, 0) ) {
+ const macho_header<Pointer64<LittleEndian> >* header = (const macho_header<Pointer64<LittleEndian> >*)fileContent;
*result = CPU_TYPE_ARM64;
*subResult = CPU_SUBTYPE_ARM64_ALL;
+ *platform = Parser<arm64>::findPlatform(header);
return true;
}
return false;
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<const char*> &syms)
+{
+ if ( mach_o::relocatable::Parser<x86_64>::validFile(fileContent) ) {
+ return mach_o::relocatable::Parser<x86_64>::getNonLocalSymbols(fileContent, syms);
+ }
+ else if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, false, 0) ) {
+ return mach_o::relocatable::Parser<arm>::getNonLocalSymbols(fileContent, syms);
+ }
+ else if ( mach_o::relocatable::Parser<x86>::validFile(fileContent, false, 0) ) {
+ return mach_o::relocatable::Parser<x86>::getNonLocalSymbols(fileContent, syms);
+ }
+ else if ( mach_o::relocatable::Parser<arm64>::validFile(fileContent, false, 0) ) {
+ return mach_o::relocatable::Parser<arm64>::getNonLocalSymbols(fileContent, syms);
+ }
+ return false;
+}
+
} // namespace relocatable
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,
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<const char*> &syms);
} // namespace relocatable
} // namespace mach_o
#include <vector>
+#include <map>
#include "ld.hpp"
#include "opaque_section_file.h"
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; }
--- /dev/null
+/* -*- 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 <sys/param.h>
+#include <sys/mman.h>
+
+#include <vector>
+
+#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<Token> _allowedClients;
+ std::vector<Token> _reexportedLibraries;
+ std::vector<Token> _symbols;
+ std::vector<Token> _classes;
+ std::vector<Token> _ivars;
+ std::vector<Token> _weakDefSymbols;
+ std::vector<Token> _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<void (Token)> 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 <typename A> class File;
+
+
+//
+// An ExportAtom has no content. It exists so that the linker can track which imported
+// symbols came from which dynamic libraries.
+//
+template <typename A>
+class ExportAtom : public ld::Atom
+{
+public:
+ ExportAtom(const File<A>& f, const char* nm, bool weakDef, bool tlv)
+ : 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<A>& _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 <typename A>
+class File : public ld::dylib::File
+{
+public:
+ static bool validFile(const uint8_t* fileContent, bool executableOrDylib);
+ File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
+ time_t mTime, 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<const char*>* 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<A>;
+
+ 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<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals> NameToAtomMap;
+ typedef std::unordered_set<const char*, CStringHash, ld::CStringEquals> NameSet;
+
+ struct Dependent { const char* path; File<A>* dylib; };
+
+ virtual std::pair<bool, bool> 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<Dependent> _dependentDylibs;
+ std::vector<const char*> _allowableClients;
+ mutable NameToAtomMap _atoms;
+ NameSet _ignoreExports;
+ bool _noRexports;
+ bool _hasWeakExports;
+ bool _hasPublicInstallName;
+ mutable bool _providedAtom;
+ bool _wrongOS;
+ bool _installPathOverride;
+ bool _indirectDylibsProcessed;
+ std::unique_ptr<ld::Bitcode> _bitcode;
+ static bool _s_logHashtable;
+};
+
+template <typename A>
+bool File<A>::_s_logHashtable = false;
+
+
+template <typename A>
+File<A>::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()));
+
+ // <rdar://problem/20659505> [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 <typename A>
+void File<A>::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 <typename A>
+void File<A>::addSymbol(const char* name, bool weakDef, bool tlv)
+{
+ // symbols that start with $ld$ are meta-data to the static linker
+ // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
+ if ( strncmp(name, "$ld$", 4) == 0 ) {
+ // $ld$ <action> $ <condition> $ <symbol-name>
+ const char* symAction = &name[4];
+ const char* symCond = strchr(symAction, '$');
+ if ( symCond != 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 <typename A>
+bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
+{
+ handler.doFile(*this);
+ return false;
+}
+
+
+template <typename A>
+std::pair<bool, bool> File<A>::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 <typename A>
+bool File<A>::hasWeakDefinition(const char* name) const
+{
+ // if supposed to ignore this export, then pretend I don't have it
+ if ( _ignoreExports.count(name) != 0 )
+ return false;
+
+ return hasWeakDefinitionImpl(name).second;
+}
+
+
+// <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
+template <typename A>
+bool File<A>::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 <typename A>
+bool File<A>::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 <typename A>
+bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const
+{
+ // if supposed to ignore this export, then pretend I don't have it
+ if ( _ignoreExports.count(name) != 0 )
+ return false;
+
+
+ AtomAndWeak bucket;
+ uint64_t addr;
+ if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, addr) ) {
+ bucket.atom = new ExportAtom<A>(*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 <typename A>
+bool File<A>::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 <typename A>
+void File<A>::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<A>*)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<A>*)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 <typename A>
+void File<A>::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 <typename A>
+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<A>(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 <typename A>
+bool Parser<A>::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<x86_64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
+ return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
+ break;
+#endif
+#if SUPPORT_ARCH_i386
+ case CPU_TYPE_I386:
+ if ( Parser<x86>::validFile(fileContent, fileLength, path, opts.architectureName()) )
+ return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
+ break;
+#endif
+#if SUPPORT_ARCH_arm_any
+ case CPU_TYPE_ARM:
+ if ( Parser<arm>::validFile(fileContent, fileLength, path, opts.architectureName()) )
+ return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
+ break;
+#endif
+#if SUPPORT_ARCH_arm64
+ case CPU_TYPE_ARM64:
+ if ( Parser<arm64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
+ return Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
+ break;
+#endif
+ }
+ return nullptr;
+}
+
+
+} // namespace dylib
+} // namespace textstub
+
+
--- /dev/null
+/* -*- 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__
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <vector>
+#include <dlfcn.h>
+#include <math.h>
+#include <unistd.h>
+#include <time.h>
+#include <unordered_map>
+
+#include "llvm-c/lto.h"
+// c header
+extern "C" {
+#include <xar/xar.h>
+}
+
+#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<FileHandler*> _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<const char*> 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<std::string, BundleHandler*> 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<ld::AsmBitcode*>(f->getBitcode()) ) {
+ ObjectHandler objHandler((char*)ab->getContent(), ab->getSize());
+ objHandler.populateMustPreserveSymbols(obfuscator);
+ } else if ( ld::BundleBitcode* bb = dynamic_cast<ld::BundleBitcode*>(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<ld::LLVMBitcode*>(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<uint8_t*>(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<ld::BundleBitcode*>(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<uint8_t*>(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<ld::AsmBitcode*>(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<std::string> 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<const char*> exports = _options.exportsData();
+ std::string exps;
+ for (std::vector<const char*>::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<char*>(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<const char*> 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
--- /dev/null
+/* -*- 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_) */
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;
island, island->name(), displacement);
++islandCount;
regionsIslands[0]->push_back(island);
+ state.atomToSection[island] = textSection;
}
else {
island = pos->second;
(*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;
}
(*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;
}
}
shims.push_back(shim);
thumbToAtomMap[target] = shim;
+ state.atomToSection[shim] = sect;
}
else {
shim = pos->second;
shim = new ARMtoThumbShimAtom(target, *sect);
shims.push_back(shim);
atomToThumbMap[target] = shim;
+ state.atomToSection[shim] = sect;
}
else {
shim = pos->second;
#include <mach/machine.h>
#include <vector>
+#include <map>
#include "ld.hpp"
#include "dylibs.h"
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),
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
|| (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:
// don't create GOT atoms during this loop because that could invalidate the sections iterator
std::vector<const ld::Atom*> atomsReferencingGOT;
std::map<const ld::Atom*,bool> weakImportMap;
+ std::map<const ld::Atom*,bool> weakDefMap;
atomsReferencingGOT.reserve(128);
for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
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
}
if ( gotMap.count(targetOfGOT) == 0 )
gotMap[targetOfGOT] = NULL;
+ // record if target is weak def
+ weakDefMap[targetOfGOT] = targetIsExternalWeakDef;
// record weak_import attribute
std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(targetOfGOT);
if ( pos == weakImportMap.end() ) {
// 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);
}
}
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
#include <mach/machine.h>
#include <vector>
+#include <map>
#include "ld.hpp"
#include "huge.h"
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;
std::set<const ld::Atom*> nlcatListAtoms;
for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
- if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) ) {
+ if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strncmp(sect->segmentName(), "__DATA", 6) == 0) ) {
for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
const ld::Atom* categoryListElementAtom = *ait;
for (unsigned int offset=0; offset < categoryListElementAtom->size(); offset += sizeof(pint_t)) {
}
}
// 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;
}
const ld::Atom* newClassRO = Class<A>::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
const ld::Atom* newClassRO = Class<A>::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
const ld::Atom* newMetaClassRO = Class<A>::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
const ld::Atom* newClassRO = Class<A>::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;
}
}
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;
break;
}
}
+ // update atom-to-section map
+ for (std::set<const ld::Atom*>::iterator it=moveToData.begin(); it != moveToData.end(); ++it) {
+ _state.atomToSection[*it] = dataSect;
+ }
}
}
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),
_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);
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;
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 {
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),
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); }
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); }
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),
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;
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)
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<const ld::dylib::File*>(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 )
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);
}
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
}
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(); }
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;
continue;
for (std::vector<const ld::Atom*>::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");
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;
}
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);
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;
const macho_segment_command<P>* fFirstWritableSegment;
bool fWriteableSegmentWithAddrOver4G;
std::vector<const macho_segment_command<P>*>fSegments;
+ std::vector<const macho_section<P>*> fSections;
std::vector<const char*> fDylibs;
std::vector<const macho_dylib_command<P>*> fDylibLoadCommands;
+ macho_section<P> fMachHeaderPseudoSection;
};
fPath = strdup(path);
fHeader = (const macho_header<P>*)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<P>) + fHeader->sizeofcmds();
if ( segCmd->vmaddr() > 0x100000000ULL )
fWriteableSegmentWithAddrOver4G = true;
}
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect)
+ fSections.push_back(sect);
}
break;
case LC_LOAD_DYLIB:
}
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;
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);
template <typename A>
-const uint8_t* DyldInfoPrinter<A>::printSharedRegionInfoForEachULEB128Address(const uint8_t* p, uint8_t kind)
+const uint8_t* DyldInfoPrinter<A>::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 = "<<unknown>>";
}
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;
}
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 :== <count> FromToSection+
+ // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+ // ToOffset :== <to-sect-offset-delta> <count> FromOffset+
+ // FromOffset :== <kind> <count> <from-sect-offset-delta>
+ 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<P>* fromSection = fSections[fromSectionIndex];
+ const macho_section<P>* 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 <typename A>
+const char* DyldInfoPrinter<A>::sharedRegionKindName(uint8_t kind)
+{
+ switch (kind) {
+ default:
+ return "<<unknown>>";
+ 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<arm>::printFunctionStartLine(uint64_t addr)
const uint8_t* start = ((uint8_t*)fHeader + fDRInfo->dataoff());
//const uint8_t* end = ((uint8_t*)fHeader + fDRInfo->dataoff() + fDRInfo->datasize());
typedef Security::SuperBlob<Security::kSecCodeMagicDRList> DRListSuperBlob;
- typedef Security::SuperBlob<Security::kSecCodeMagicRequirementSet> InternalRequirementsSetBlob;
const DRListSuperBlob* topBlob = (DRListSuperBlob*)start;
if ( topBlob->validateBlob(fDRInfo->datasize()) ) {
if ( topBlob->count() == fDylibLoadCommands.size() ) {
}
template <typename A>
-const char* DyldInfoPrinter<A>::symbolNameForAddress(uint64_t addr)
+const char* DyldInfoPrinter<A>::closestSymbolNameForAddress(uint64_t addr, uint64_t* offset, uint8_t sectIndex)
{
+ const macho_nlist<P>* bestSymbol = NULL;
if ( fDynamicSymbolTable != NULL ) {
- // find exact match in globals
- const macho_nlist<P>* lastExport = &fSymbols[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()];
- for (const macho_nlist<P>* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) {
- if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) {
- return &fStrings[sym->n_strx()];
+ // find closest match in globals
+ const macho_nlist<P>* const globalsStart = &fSymbols[fDynamicSymbolTable->iextdefsym()];
+ const macho_nlist<P>* const globalsEnd = &globalsStart[fDynamicSymbolTable->nextdefsym()];
+ for (const macho_nlist<P>* 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<P>* lastLocal = &fSymbols[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()];
- for (const macho_nlist<P>* sym = &fSymbols[fDynamicSymbolTable->ilocalsym()]; sym < lastLocal; ++sym) {
- if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) {
- return &fStrings[sym->n_strx()];
+
+ // find closest match in locals
+ const macho_nlist<P>* const localsStart = &fSymbols[fDynamicSymbolTable->ilocalsym()];
+ const macho_nlist<P>* const localsEnd = &localsStart[fDynamicSymbolTable->nlocalsym()];
+ for (const macho_nlist<P>* 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<P>* lastSym = &fSymbols[fSymbolCount];
- for (const macho_nlist<P>* 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<P>* const allStart = &fSymbols[0];
+ const macho_nlist<P>* const allEnd = &fSymbols[fSymbolCount];
+ for (const macho_nlist<P>* 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 <typename A>
+const char* DyldInfoPrinter<A>::symbolNameForAddress(uint64_t addr)
+{
+ uint64_t offset;
+ const char* s = closestSymbolNameForAddress(addr, &offset);
+ if ( (offset == 0) && (s != NULL) )
+ return s;
return "?";
}
-
+
template <typename A>
void DyldInfoPrinter<A>::printClassicBindingInfo()
{
"\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"
);
}
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
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)
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
--- /dev/null
+##
+# 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
--- /dev/null
+#include <stdio.h>
+
+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);
#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
* @APPLE_LICENSE_HEADER_END@
*/
+#include <stdio.h>
+
// work around until compiler supports __thread
extern int* get_a();
extern int* get_b();
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;
}