+-------- tagged ld64-127.2
+
+2011-08-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9945513> suppress version load command for simulator builds
+
+-------- tagged ld64-127.1
+
+2011-07-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9847280> Csu needs to support for armv7 variants
+
+-------- tagged ld64-127
+
+2011-07-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9838090> crash with TLS + -dead_strip
+
+2011-07-20 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9778727> ld64-123.2.1/ChangeLog contains internal train names and radar titles
+
+2011-07-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9777977> ld crashes with an assertion failure when linking WebKit with LTO
+
+2011-07-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9779759> Personalities missing when using compact unwind
+
+2011-07-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9740166> force loaded archives not listed in LD_TRACE
+
+2011-07-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9716724> spurious warning: Codegen prevents image from working in dyld shared cache
+
+2011-07-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9707126> Fix -classic_linker option
+
+-------- tagged ld64-126.5
+
+2011-06-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9618702> ld64-124.6: ld -r introduces duplicate symbols
+
+2011-06-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9610466> loosen check for 32-bit absolute address out of range
+
+-------- tagged ld64-126.3.1
+
+2011-06-15 Nick Kledzik <kledzik@apple.com>
+
+ Update armv7 variants
+
+-------- tagged ld64-126.2
+
+2011-06-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9461567> iOS ld -r loses dont-dead-strip attribute on __objc_nlclslist section
+
+2011-06-13 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9231829> LC_ENCRYPTION_INFO size can be wrong
+
+
+-------- tagged ld64-126.1
+
+2011-06-10 Nick Kledzik <kledzik@apple.com>
+
+ Add back support for armv7 variants
+
+-------- tagged ld64-126
+
+2011-06-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9581690> -ObjC does not work for simulator
+
+2011-06-09 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9551362> clang ld: bad codegen, pointer diff
+ Added test case: unit-tests/test-cases/weak-def-hidden-and-global
+
+2011-06-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9553065> warning then assertion when libSystem.dylib is missing
+
+2011-06-02 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9544194> ld crash with resolver functions
+
+2011-06-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/7259423> define way for compilers to specify compact unwind info
+ Added test case: unit-tests/test-cases/compact-unwind-basic
+ Updated unwinddump tool to display compact unwind info in .o files
+
+2011-06-01 Nick Kledzik <kledzik@apple.com>
+
+ Allow 8612550 (turn ordered zero fill symbols into zero data) to work not just for dyld
+
+2011-06-01 Nick Kledzik <kledzik@apple.com>
+
+ Remove trailing /. in dwarf source dirs to cannoicalize paths
+
+2011-06-01 Nick Kledzik <kledzik@apple.com>
+
+ Sort debug notes by output order instead of input order.
+
+2011-06-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9537755> remove support for invoking ld_classic in iOS
+
+2011-06-01 Nick Kledzik <kledzik@apple.com>
+
+ Fix arm branch interworking in -r for armv6
+
+2011-06-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9521882> i386 regression with pointer-diff of same pointer
+
+2011-05-27 Nick Kledzik <kledzik@apple.com>
+
+ Canonicalize dwarf source file dirname to always end in /
+
+2011-05-27 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9513487> support arm branch interworking in -r mode (use extern relocs)
+
+2011-05-27 Nick Kledzik <kledzik@apple.com>
+
+ Add -page_align_data_atoms option
+
+2011-05-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9292295> align(16384) doesn't produce 16K aligned globals on ARMv7
+
+2011-05-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9493908> support arm shims in sections other than __text
+
+2011-05-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8750693> ld64 should only install to the platform in iOS
+
+2011-05-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9452006> Ld assertion with unusual section order
+
+2011-05-17 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9441273> Linker is not automatically weak loading dylibs when all references are weak
+
+-------- tagged ld64-125.3
+
+2011-05-12 Nick Kledzik <kledzik@apple.com>
+
+ Fix missing split-seg-info for kindSetTargetImageOffset
+
+2011-05-12 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9420745> Linker crashes with __gcc_except_tab data belonging to no FDE
+
+2011-05-11 Nick Kledzik <kledzik@apple.com>
+
+ Fix nop padding for arm code
+
+2011-05-05 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9394006> x86_64: cmp of GOT slot loses weak_import bit
+
+-------- tagged ld64-125.2
+
+2011-05-02 Nick Kledzik <kledzik@apple.com>
+
+ Fix -flat_namespace issue with not all indirect dylibs being processed
+
+2011-04-29 Nick Kledzik <kledzik@apple.com>
+
+ Fix sign extention on i386 addends of extern vanilla relocs
+
+2011-04-29 Nick Kledzik <kledzik@apple.com>
+
+ Don't let -ObjC double load any archive members
+
+2011-04-29 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9356572> better warning about unaligned ARM functions
+
+-------- tagged ld64-125.1
+
+2011-04-28 Nick Kledzik <kledzik@apple.com>
+
+ Fix sign extention on arm sect-diff relocs so as to not trip rangeCheckAbsolute32()
+
+-------- tagged ld64-125
+
+2011-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8866673> the entry point should start out initially undefined
+
+2011-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/5804214> ld should never have a symbol in the non-lazy indirect symbol table with index 0
+
+2011-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/6978069> ld adds undefined symbol from .exp file to kext bundle
+
+2011-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/7664544> Linker typo suggestions should ignore l- and L- symbols
+
+2011-04-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/7952137> -order_file_statistics warns about syms in multiple .o files even when names in order file are prefixed
+
+2011-04-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8642343> warning when a method is overridden in a category in the same link unit
+ Add test case: unit-tests/test-cases/objc-category-warning
+
+2011-04-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/7890410> don't let function from archive override a tentative definition
+ Add test case: unit-tests/test-cases/tentative-and-archive-code
+
+2011-04-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8822887> x86_64 -- lossy relocation at static link time (push/mov $imm)
+
+2011-04-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8989530> Add comment to error message when __ZTV symbols are undefined
+
+2011-04-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8995535> obsolete -no_compact_linkedit
+
+2011-04-23 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9281002> sect->sectname() passed to "%s" formats
+
+2011-04-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9275707> linking a sub library of libSystem should not warn about common symbols
+
+2011-04-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9282815> support movw/movt in static executables
+
+2011-04-12 Nick Kledzik <kledzik@apple.com>
+
+ Rework ARM subtype handling to be table driven
+
+2011-04-11 Nick Kledzik <kledzik@apple.com>
+
+ Error if -init or -e function not in image being linked
+
+2011-04-01 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9216420> -static and -stack_addr don't work together
+
+2011-03-31 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9183821> ld assert in LTO mode if libLTO suppresses a weak symbol it should have perserved
+
+-------- tagged ld64-124.1
+
+2011-03-30 Nick Kledzik <kledzik@apple.com>
+
+ log warning if ld_classic is invoked
+
+2011-03-30 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9209135> Support "-arch arm -force_cpusubtype_ALL" to keep gcc building
+
+-------- tagged ld64-124
+
+2011-03-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9144456> make libgcc_s and libSystem work for any link order
+
+2011-03-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8750693> ld64 should only install to the platform in iOS trains
+
+2011-03-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9127471> ld64 should build stand-alone and not need libunwind headers
+
+2011-03-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9139782> add LC_VERSION_MIN_IPHONEOS to iOS targets, warn on mismatches
+
+2011-03-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8836481> Make iOS simulator a real platform with command line versioning
+
+2011-03-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8964869> static executables don't get function start information
+
+2011-03-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9112113> allow_sub_type_mismatches linker flag broken
+
+2011-03-15 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9106345> Add option to support merging zero fill sections
+ Add test case: unit-tests/test-cases/merge_zero_fill_sections
+
+2011-03-15 Nick Kledzik <kledzik@apple.com>
+
+ Improve error message about text-relocs caused by direct access to global weak symbols.
+
+2011-03-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9116044> ld assert linking armv7 kext bundle on b/bl to external function
+
+-------- tagged ld64-123.10
+
+2011-03-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9085618> linking x86_64 causes assert from changes in ld64-123.9
+
+-------- tagged ld64-123.9
+
+2011-03-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9055754> movw/movt don't work in dyld shared cache
+
+2011-03-03 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8995525> classic linkedit does not match compact for non-lazy pointers
+
+2011-02-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/9052679> Support armv7 variants
+
+-------- tagged ld64-123.8
+
+2011-02-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8926992> Switch arm32 kexts to MH_KEXT_BUNDLE
+
+-------- tagged ld64-123.7
+
+2011-02-10 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8926992> Switch arm32 kexts to MH_KEXT_BUNDLE, if LD_KEXT_BUNDLE is set
+
+2011-01-28 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8931747> spurious 'found branch-22 without store' warning
+
+-------- tagged ld64-123.6
+
+2011-01-26 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8904405> crash with arm hi16/lo16 to external symbols
+
+-------- tagged ld64-123.5
+
+2011-01-24 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8910802> dyld synthesized tail call stubs don't always work
+
+-------- tagged ld64-123.4
+
+2011-01-19 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8866345> __text with >10 alignment should disable close-stub optimization
+
+2011-01-18 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8877072> :upper16: / :lower16: not working when targeting thumb functions
+
+-------- tagged ld64-123.3
+
+2010-12-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8764917> ld64 making shims when not necessary
+
+2010-12-14 Nick Kledzik <kledzik@apple.com>
+
+ <rdar://problem/8760268> Add work around for latest llvm-c/lto.h
+
-------- tagged ld64-123.2.1
-2010-03-07 Nick Kledzik <kledzik@apple.com>
+2011-03-07 Nick Kledzik <kledzik@apple.com>
<rdar://problem/8955206> enable i386 ASLR
2010-11-01 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/8612861>
+ <rdar://problem/8612861> iOS is missing dof sections for armv7 slice
-------- tagged ld64-120.3
2010-08-20 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/8309595> SWB: ld64-117.1 on Durango8F54: Assertion failed:
+ <rdar://problem/8309595> SWB: ld64-117.1 on 8F54: Assertion failed:
UTF16 CFStrings were not coalesced correctly when gcc built the .o files and the
last string in the __ustring section only had a single zero byte at the end.
2010-08-14 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/8309530> SWB: ld64-117.1 on Durango8F54: Assertion failed: (categoryAtom->size() == Category<A>::size())
+ <rdar://problem/8309530> SWB: ld64-117.1 on 8F54: Assertion failed: (categoryAtom->size() == Category<A>::size())
gcc-4.0 uses 'L' labels on categories. This merges them onto previous data and disable category optimzation
2010-08-14 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/8309917> SWB: ld64-117.1 on Durango8F54: bad category optimization
+ <rdar://problem/8309917> SWB: ld64-117.1 on 8F54: bad category optimization
Disable category optimization for i386 and arm until further testing
2010-08-14 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/8309608> SWB: ld64-117.1 on Durango8F54: address not in any section
+ <rdar://problem/8309608> SWB: ld64-117.1 on 8F54: address not in any section
Handle pointer diff to stub for weak hidden function
2010-08-13 Nick Kledzik <kledzik@apple.com>
2010-06-09 Nick Kledzik <kledzik@apple.com>
- <rdar://problem/8076986>
+ <rdar://problem/8076986> 'rebase' makes timestamps invalid/unreadable for GDB
2010-06-09 Nick Kledzik <kledzik@apple.com>
By default the linker moves all zero fill sections to the end of the __DATA segment and configures
them to use no space on disk. This option suppresses that optimization, so zero-filled data occupies
space on disk in a final linked image.
+.It Fl merge_zero_fill_sections
+Causes all zero-fill sections in the __DATA segment to be merged into one __zerofill section.
.El
.Ss Options when creating a dynamic library (dylib)
.Bl -tag
.Bl -tag
.It Fl v
Prints the version of the linker.
-.It Fl no_compact_linkedit
-Normally when targeting Mac OS X 10.6, the linker will generate compact information
-in the __LINKEDIT segment.
-This option causes the linker to instead produce traditional relocation information.
.It Fl allow_heap_execute
Normally i386 main executables will be marked so that the Mac OS X 10.7 and later kernel
will only allow pages with the x-bit to execute instructions. This option overrides that
is complete. Without the option, the linker picks a path and deletes the object file before the linker
tool completes, thus tools such as the debugger or dsymutil will not be able to access the DWARF debug
info in the temporary object file.
+.It Fl page_align_data_atoms
+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).
.El
.Ss Obsolete Options
.Bl -tag
isa = PBXAggregateTarget;
buildConfigurationList = F9B1A26C0A3A568700DA8FAB /* Build configuration list for PBXAggregateTarget "all" */;
buildPhases = (
+ F9871A3413340B4600DB3F24 /* Platform install */,
);
dependencies = (
F9B1A2690A3A568200DA8FAB /* PBXTargetDependency */,
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/csh;
- shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0";
+ shellScript = "# Let tests set MACOSX_DEPLOYMENT_TARGET as they need\nunsetenv MACOSX_DEPLOYMENT_TARGET\n\n# make linker relative libLTO.dylib\nmkdir -p ${BUILD_DIR}/lib\nln -sf /Developer/usr/lib/libLTO.dylib ${BUILD_DIR}/lib/libLTO.dylib\n\n# always use new linker\nsetenv LD_NO_CLASSIC_LINKER\nsetenv LD_NO_CLASSIC_LINKER_STATIC\n\n# run full test suite\n\"$SRCROOT\"/unit-tests/run-all-unit-tests\n\nexit 0";
+ showEnvVarsInLog = 0;
+ };
+ F9871A3413340B4600DB3F24 /* Platform install */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Platform install";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "\nif [ -n \"${RC_PURPLE}\" ]; then\n\techo \"here\"\n\tmkdir -p ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer/\n\tmv ${DSTROOT}/usr ${DSTROOT}/Developer/Platforms/iPhoneOS.platform/Developer\nfi\n";
showEnvVarsInLog = 0;
};
F9E8DB4D11921594007B4D6A /* make config.h */ = {
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
+ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
HEADER_SEARCH_PATHS = "";
INSTALL_PATH = /usr/bin;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = s;
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
+ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/src/ld",
"$(DEVELOPER_DIR)/usr/local/include",
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
INSTALL_PATH = "$(HOME)/bin";
PREBINDING = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
+ GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
HEADER_SEARCH_PATHS = "";
INSTALL_PATH = /usr/bin;
#include <mach-o/arm/reloc.h>
#include <mach-o/compact_unwind_encoding.h>
#include <mach/machine.h>
+#include <stddef.h>
#include "FileAbstraction.hpp"
#ifndef CPU_SUBTYPE_ARM_V7
#define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
#endif
+
#ifndef ARM_THUMB_32BIT_BRANCH
#define ARM_THUMB_32BIT_BRANCH 7
#endif
#define ARM_RELOC_HALF 8
#define ARM_RELOC_HALF_SECTDIFF 9
+#ifndef CPU_SUBTYPE_ARM_V7F
+ #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10)
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7K
+ #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12)
+#endif
+
+struct ARMSubType {
+ const char* subTypeName;
+ const char* llvmTriplePrefix;
+ cpu_subtype_t subType;
+ bool supportsThumb2;
+};
+
+static const ARMSubType ARMSubTypes[] = {
+ { "armv4t","armv4t-", CPU_SUBTYPE_ARM_V4T, false },
+ { "armv5", "armv5e-", CPU_SUBTYPE_ARM_V5TEJ, false },
+ { "armv6", "armv6-", CPU_SUBTYPE_ARM_V6, false },
+ { "armv7", "thumbv7-", CPU_SUBTYPE_ARM_V7, true },
+ { "armv7f", "thumbv7f-", CPU_SUBTYPE_ARM_V7F, true },
+ { "armv7k", "thumbv7k-", CPU_SUBTYPE_ARM_V7K, true },
+ { 0, NULL, false }
+};
+
+
+
//
// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness
};
+//
+// mach-o __LD, __compact_unwind section in object files
+//
+template <typename P>
+class macho_compact_unwind_entry {
+public:
+ typedef typename P::E E;
+ typedef typename P::uint_t pint_t;
+
+ pint_t codeStart() const INLINE { return P::getP(_codeStart); }
+ void set_codeStart(pint_t value) INLINE { P::setP(_codeStart, value); }
+
+ uint32_t codeLen() const INLINE { return E::get32(_codeLen); }
+ void set_codeLen(uint32_t value) INLINE { E::set32(_codeLen, value); }
+
+ uint32_t compactUnwindInfo() const INLINE { return E::get32(_compactUnwindInfo); }
+ void set_compactUnwindInfo(uint32_t value) INLINE { E::set32(_compactUnwindInfo, value); }
+
+ pint_t personality() const INLINE { return P::getP(_personality); }
+ void set_personality(pint_t value) INLINE { P::setP(_personality, value); }
+
+ pint_t lsda() const INLINE { return P::getP(_lsda); }
+ void set_lsda(pint_t value) INLINE { P::setP(_lsda, value); }
+
+ static uint32_t codeStartFieldOffset() INLINE { return offsetof(macho_compact_unwind_entry<P>,_codeStart); }
+ static uint32_t personalityFieldOffset() INLINE { return offsetof(macho_compact_unwind_entry<P>,_personality); }
+ static uint32_t lsdaFieldOffset() INLINE { return offsetof(macho_compact_unwind_entry<P>,_lsda); }
+
+private:
+ pint_t _codeStart;
+ uint32_t _codeLen;
+ uint32_t _compactUnwindInfo;
+ pint_t _personality;
+ pint_t _lsda;
+};
+
#endif // __MACH_O_FILE_ABSTRACTION__
return S_REGULAR | S_ATTR_NO_DEAD_STRIP;
else if ( (strncmp(sect->sectionName(), "__objc_superrefs", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) )
return S_REGULAR | S_ATTR_NO_DEAD_STRIP;
+ else if ( (strncmp(sect->sectionName(), "__objc_nlclslist", 16) == 0) && (strcmp(sect->segmentName(), "__DATA") == 0) )
+ return S_REGULAR | S_ATTR_NO_DEAD_STRIP;
else
return S_REGULAR;
case ld::Section::typeCode:
case ld::Section::typeLastSection:
assert(0 && "typeLastSection should not make it to final linked image");
return S_REGULAR;
+ case ld::Section::typeDebug:
+ return S_REGULAR | S_ATTR_DEBUG;
}
return S_REGULAR;
}
uint8_t* HeaderAndLoadCommandsAtom<A>::copyVersionLoadCommand(uint8_t* p) const
{
macho_version_min_command<P>* cmd = (macho_version_min_command<P>*)p;
- ld::MacVersionMin macVersion = _options.macosxVersionMin();
- ld::IPhoneVersionMin iphoneOSVersion = _options.iphoneOSVersionMin();
- assert( (macVersion != ld::macVersionUnset) || (iphoneOSVersion != ld::iPhoneVersionUnset) );
+ 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>));
else {
cmd->set_cmd(LC_VERSION_MIN_IPHONEOS);
cmd->set_cmdsize(sizeof(macho_version_min_command<P>));
- cmd->set_version((uint32_t)iphoneOSVersion);
+ cmd->set_version((uint32_t)iOSVersion);
cmd->set_reserved(0);
}
return p + sizeof(macho_version_min_command<P>);
{
uint32_t sz = alignedSize(sizeof(macho_dylib_command<P>) + strlen(dylib->installPath()) + 1);
macho_dylib_command<P>* cmd = (macho_dylib_command<P>*)p;
- // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
- bool autoWeakLoadDylib = false; // FIX
- //( (fWriter.fDylibReadersWithWeakImports.count(fInfo.reader) > 0)
- //&& (fWriter.fDylibReadersWithNonWeakImports.count(fInfo.reader) == 0) );
if ( dylib->willBeLazyLoadedDylib() )
cmd->set_cmd(LC_LAZY_LOAD_DYLIB);
- else if ( dylib->willBeWeakLinked() || autoWeakLoadDylib )
+ else if ( dylib->forcedWeakLinked() || dylib->allSymbolsAreWeakImported() )
cmd->set_cmd(LC_LOAD_WEAK_DYLIB);
else if ( dylib->willBeReExported() && _options.useSimplifiedDylibReExports() )
cmd->set_cmd(LC_REEXPORT_DYLIB);
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
*
- * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
}
-ld::File* InputFiles::makeFile(const Options::FileInfo& info)
+ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib)
{
// map in whole file
uint64_t len = info.fileLen;
// see if it is an object file
mach_o::relocatable::ParserOptions objOpts;
objOpts.architecture = _options.architecture();
- objOpts.objSubtypeMustMatch = _options.preferSubArchitecture();
+ objOpts.objSubtypeMustMatch = !_options.allowSubArchitectureMismatches();
objOpts.logAllFiles = _options.logAllFiles();
objOpts.convertUnwindInfo = _options.needsUnwindInfoSection();
objOpts.subType = _options.subArchitecture();
return this->addObject(objResult, info, len);
// see if it is a dynamic library
- ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, _nextInputOrdinal, info.options.fBundleLoader);
+ ld::dylib::File* dylibResult = mach_o::dylib::parse(p, len, info.path, info.modTime, _options, _nextInputOrdinal, info.options.fBundleLoader, indirectDylib);
if ( dylibResult != NULL )
return this->addDylib(dylibResult, info, len);
// see if it is a static library
- archive::ParserOptions archOpts;
+ ::archive::ParserOptions archOpts;
archOpts.objOpts = objOpts;
archOpts.forceLoadThisArchive = info.options.fForceLoad;
archOpts.forceLoadAll = _options.fullyLoadArchives();
archOpts.forceLoadObjC = _options.loadAllObjcObjectsFromArchives();
+ archOpts.objcABI2 = _options.objCABIVersion2POverride();
archOpts.verboseLoad = _options.whyLoad();
archOpts.logAllFiles = _options.logAllFiles();
- ld::File* archiveResult = archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts);
- if ( archiveResult != NULL )
+ ld::archive::File* archiveResult = ::archive::parse(p, len, info.path, info.modTime, _nextInputOrdinal, archOpts);
+ if ( archiveResult != NULL ) {
+ // <rdar://problem/9740166> force loaded archives should be in LD_TRACE
+ if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && _options.traceArchives() )
+ logArchive(archiveResult);
return this->addArchive(archiveResult, info, len);
-
+ }
+
// does not seem to be any valid linker input file, check LTO misconfiguration problems
if ( lto::archName((uint8_t*)p, len) != NULL ) {
if ( lto::libLTOisLoaded() ) {
if ( strcmp(dit->installName,installPath) == 0 ) {
try {
Options::FileInfo info = _options.findFile(dit->useInstead);
- ld::File* reader = this->makeFile(info);
+ ld::File* reader = this->makeFile(info, true);
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
//_installPathToDylibs[strdup(installPath)] = dylibReader;
// search for dylib using -F and -L paths
Options::FileInfo info = _options.findFileUsingPaths(installPath);
try {
- ld::File* reader = this->makeFile(info);
+ ld::File* reader = this->makeFile(info, true);
ld::dylib::File* dylibReader = dynamic_cast<ld::dylib::File*>(reader);
if ( dylibReader != NULL ) {
//assert(_installPathToDylibs.find(installPath) != _installPathToDylibs.end());
// keep processing dylibs until no more dylibs are added
unsigned long lastMapSize = 0;
- std::set<ld::dylib::File*> dylibsProcessed;
+ std::set<ld::dylib::File*> dylibsProcessed;
while ( lastMapSize != _allDylibs.size() ) {
lastMapSize = _allDylibs.size();
// can't iterator _installPathToDylibs while modifying it, so use temp buffer
for (std::vector<Options::FileInfo>::const_iterator it = files.begin(); it != files.end(); ++it) {
const Options::FileInfo& entry = *it;
try {
- _inputFiles.push_back(this->makeFile(entry));
+ _inputFiles.push_back(this->makeFile(entry, false));
}
catch (const char* msg) {
if ( (strstr(msg, "architecture") != NULL) && !_options.errorOnOtherArchFiles() ) {
}
// store options about how dylib will be used in dylib itself
if ( info.options.fWeakImport )
- reader->setWillBeWeakLinked();
+ reader->setForcedWeakLinked();
if ( info.options.fReExport )
reader->setWillBeReExported();
if ( info.options.fUpward ) {
}
if ( didSomething || true ) {
switch ( _options.outputKind() ) {
+ case Options::kStaticExecutable:
case Options::kDynamicExecutable:
// add implicit __dso_handle label
handler.doAtom(DSOHandleAtom::_s_atomExecutable);
handler.doAtom(DSOHandleAtom::_s_atomDyld);
handler.doAtom(DSOHandleAtom::_s_atomAll);
break;
- case Options::kStaticExecutable:
- // add implicit __dso_handle label
- handler.doAtom(DSOHandleAtom::_s_atomExecutable);
- handler.doAtom(DSOHandleAtom::_s_atomAll);
- break;
case Options::kPreload:
// add implicit __mh_preload_header label
handler.doAtom(DSOHandleAtom::_s_atomPreload);
}
-bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler& handler) const
+bool InputFiles::searchLibraries(const char* name, bool searchDylibs, bool searchArchives, bool dataSymbolOnly, ld::File::AtomHandler& handler) const
{
// check each input file
for (std::vector<ld::File*>::const_iterator it=_inputFiles.begin(); it != _inputFiles.end(); ++it) {
// if this reader is a static archive that has the symbol we need, pull in all atoms in that module
// if this reader is a dylib that exports the symbol we need, have it synthesize an atom for us.
ld::dylib::File* dylibFile = dynamic_cast<ld::dylib::File*>(file);
+ ld::archive::File* archiveFile = dynamic_cast<ld::archive::File*>(file);
if ( searchDylibs && (dylibFile != NULL) ) {
//fprintf(stderr, "searchLibraries(%s), looking in linked %s\n", name, dylibFile->path() );
if ( dylibFile->justInTimeforEachAtom(name, handler) ) {
// else continue search for a non-weak definition
}
}
- else if ( searchArchives && (dylibFile == NULL) ) {
- if ( file->justInTimeforEachAtom(name, handler) ) {
- if ( _options.traceArchives() )
- logArchive(file);
- // found definition in static library, done
- return true;
+ else if ( searchArchives && (archiveFile != NULL) ) {
+ if ( dataSymbolOnly ) {
+ if ( archiveFile->justInTimeDataOnlyforEachAtom(name, handler) ) {
+ if ( _options.traceArchives() )
+ logArchive(file);
+ // found data definition in static library, done
+ return true;
+ }
+ }
+ else {
+ if ( archiveFile->justInTimeforEachAtom(name, handler) ) {
+ if ( _options.traceArchives() )
+ logArchive(file);
+ // found definition in static library, done
+ return true;
+ }
}
}
}
void InputFiles::dylibs(ld::Internal& state)
{
- bool dylibsOK;
+ bool dylibsOK = false;
switch ( _options.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kDynamicLibrary:
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
// iterates all atoms in initial files
bool forEachInitialAtom(ld::File::AtomHandler&) const;
// searches libraries for name
- bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives, ld::File::AtomHandler&) const;
+ bool searchLibraries(const char* name, bool searchDylibs, bool searchArchives,
+ bool dataSymbolOnly, ld::File::AtomHandler&) const;
// see if any linked dylibs export a weak def of symbol
bool searchWeakDefInDylib(const char* name) const;
// copy dylibs to link with in command line order
private:
void inferArchitecture(Options& opts, const char** archName);
const char* fileArch(const uint8_t* p, unsigned len);
- ld::File* makeFile(const Options::FileInfo& info);
+ ld::File* makeFile(const Options::FileInfo& info, bool indirectDylib);
ld::File* addDylib(ld::dylib::File* f, const Options::FileInfo& info, uint64_t mappedLen);
ld::File* addObject(ld::relocatable::File* f, const Options::FileInfo& info, uint64_t mappedLen);
ld::File* addArchive(ld::File* f, const Options::FileInfo& info, uint64_t mappedLen);
typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
- void addSplitSegInfo(uint64_t address, ld::Fixup::Kind k) const;
+ void addSplitSegInfo(uint64_t address, ld::Fixup::Kind k, uint32_t) const;
void uleb128EncodeAddresses(const std::vector<uint64_t>& locations) const;
mutable std::vector<uint64_t> _32bitPointerLocations;
mutable std::vector<uint64_t> _64bitPointerLocations;
mutable std::vector<uint64_t> _ppcHi16Locations;
+ mutable std::vector<uint64_t> _thumbLo16Locations;
+ mutable std::vector<uint64_t> _thumbHi16Locations[16];
+ mutable std::vector<uint64_t> _armLo16Locations;
+ mutable std::vector<uint64_t> _armHi16Locations[16];
static ld::Section _s_section;
ld::Section SplitSegInfoAtom<A>::_s_section("__LINKEDIT", "__splitSegInfo", ld::Section::typeLinkEdit, true);
template <>
-void SplitSegInfoAtom<x86_64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const
+void SplitSegInfoAtom<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) const
+void SplitSegInfoAtom<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) const
+void SplitSegInfoAtom<arm>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
{
switch (kind) {
case ld::Fixup::kindStoreLittleEndian32:
_32bitPointerLocations.push_back(address);
break;
+ case ld::Fixup::kindStoreARMLow16:
+ _armLo16Locations.push_back(address);
+ break;
+ case ld::Fixup::kindStoreThumbLow16:
+ _thumbLo16Locations.push_back(address);
+ break;
+ case ld::Fixup::kindStoreARMHigh16:
+ assert(extra < 16);
+ _armHi16Locations[extra].push_back(address);
+ break;
+ case ld::Fixup::kindStoreThumbHigh16:
+ assert(extra < 16);
+ _thumbHi16Locations[extra].push_back(address);
+ break;
default:
warning("codegen at address 0x%08llX prevents image from working in dyld shared cache", address);
break;
template <>
-void SplitSegInfoAtom<ppc>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const
+void SplitSegInfoAtom<ppc>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
{
switch (kind) {
case ld::Fixup::kindStorePPCPicHigh16AddLow:
template <>
-void SplitSegInfoAtom<ppc64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind) const
+void SplitSegInfoAtom<ppc64>::addSplitSegInfo(uint64_t address, ld::Fixup::Kind kind, uint32_t extra) const
{
switch (kind) {
case ld::Fixup::kindStorePPCPicHigh16AddLow:
// 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);
+ this->addSplitSegInfo(it->address, it->kind, it->extra);
}
// delta compress runs of addresses
this->_encodedData.append_byte(0); // terminator
}
+ if ( _thumbLo16Locations.size() != 0 ) {
+ this->_encodedData.append_byte(5);
+ //fprintf(stderr, "type 5:\n");
+ std::sort(_thumbLo16Locations.begin(), _thumbLo16Locations.end());
+ this->uleb128EncodeAddresses(_thumbLo16Locations);
+ this->_encodedData.append_byte(0); // terminator
+ }
+
+ if ( _armLo16Locations.size() != 0 ) {
+ this->_encodedData.append_byte(6);
+ //fprintf(stderr, "type 6:\n");
+ std::sort(_armLo16Locations.begin(), _armLo16Locations.end());
+ this->uleb128EncodeAddresses(_armLo16Locations);
+ this->_encodedData.append_byte(0); // terminator
+ }
+
+ for (uint32_t i=0; i < 16; ++i) {
+ if ( _thumbHi16Locations[i].size() != 0 ) {
+ this->_encodedData.append_byte(16+i);
+ //fprintf(stderr, "type 16+%d:\n", i);
+ std::sort(_thumbHi16Locations[i].begin(), _thumbHi16Locations[i].end());
+ this->uleb128EncodeAddresses(_thumbHi16Locations[i]);
+ this->_encodedData.append_byte(0); // terminator
+ }
+ }
+
+ for (uint32_t i=0; i < 16; ++i) {
+ if ( _armHi16Locations[i].size() != 0 ) {
+ this->_encodedData.append_byte(32+i);
+ //fprintf(stderr, "type 32+%d:\n", i);
+ std::sort(_armHi16Locations[i].begin(), _armHi16Locations[i].end());
+ this->uleb128EncodeAddresses(_armHi16Locations[i]);
+ this->_encodedData.append_byte(0); // terminator
+ }
+ }
+
// always add zero byte to mark end
this->_encodedData.append_byte(0);
&& (atom->combine() == ld::Atom::combineByName) ) {
desc |= N_REF_TO_WEAK;
}
- if ( atom->weakImported() )
+ const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(atom->file());
+ if ( atom->weakImported() || ((dylib != NULL) && dylib->forcedWeakLinked()) )
desc |= N_WEAK_REF;
entry.set_n_desc(desc);
else
sreloc1->set_r_type(GENERIC_RELOC_SECTDIFF);
sreloc1->set_r_address(address);
- if ( entry.toTarget == entry.inAtom )
- sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ if ( entry.toTarget == entry.inAtom ) {
+ if ( entry.toAddend > entry.toTarget->size() )
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.offsetInAtom);
+ else
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ }
else
sreloc1->set_r_value(entry.toTarget->finalAddress());
sreloc2->set_r_scattered(true);
else
sreloc1->set_r_type(ARM_RELOC_SECTDIFF);
sreloc1->set_r_address(address);
- if ( entry.toTarget == entry.inAtom )
- sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
- else
+ if ( entry.toTarget == entry.inAtom ) {
+ if ( entry.toAddend > entry.toTarget->size() )
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.offsetInAtom);
+ else
+ sreloc1->set_r_value(entry.toTarget->finalAddress()+entry.toAddend);
+ }
+ else {
sreloc1->set_r_value(entry.toTarget->finalAddress());
+ }
sreloc2->set_r_scattered(true);
sreloc2->set_r_pcrel(false);
sreloc2->set_r_length(2);
reloc1.set_r_symbolnum(symbolNum);
reloc1.set_r_pcrel(false);
reloc1.set_r_length(len);
- reloc1.set_r_extern(false);
+ reloc1.set_r_extern(external);
reloc1.set_r_type(ARM_RELOC_HALF);
reloc2.set_r_address(otherHalf); // other half
reloc2.set_r_symbolnum(0);
default:
throw "internal error: unexpected non-lazy pointer binding";
}
- // Special case non-lazy-pointer slot used to point to "dyld_stub_binder"
- // That slot is never bound using indirect symbol table
- if ( target == _state.compressedFastBinderProxy )
- return INDIRECT_SYMBOL_ABS;
bool targetIsGlobal = (target->scope() == ld::Atom::scopeGlobal);
switch ( target->definition() ) {
case ld::Atom::definitionRegular:
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2005-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
Options::Options(int argc, const char* argv[])
: fOutputFile("a.out"), fArchitecture(0), fSubArchitecture(0), fArchitectureName("unknown"), fOutputKind(kDynamicExecutable),
- fHasPreferredSubType(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false),
+ fHasPreferredSubType(false), fArchSupportsThumb2(false), fPrebind(false), fBindAtLoad(false), fKeepPrivateExterns(false),
fNeedsModuleTable(false), fIgnoreOtherArchFiles(false), fErrorOnOtherArchFiles(false), fForceSubtypeAll(false),
fInterposeMode(kInterposeNone), fDeadStrip(false), fNameSpace(kTwoLevelNameSpace),
fDylibCompatVersion(0), fDylibCurrentVersion(0), fDylibInstallName(NULL), fFinalName(NULL), fEntryName("start"),
fForDyld(false), fMakeTentativeDefinitionsReal(false), fWhyLoad(false), fRootSafe(false),
fSetuidSafe(false), fImplicitlyLinkPublicDylibs(true), fAddCompactUnwindEncoding(true),
fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false),
- fAutoOrderInitializers(true), fOptimizeZeroFill(true), fLogObjectFiles(false),
+ fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false),
fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false),
fOutputSlidable(false), fWarnWeakExports(false),
fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false),
fDemangle(false), fTLVSupport(false),
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070
- fVersionLoadCommand(false), fFunctionStartsLoadCommand(false),
-#else
- fVersionLoadCommand(true), fFunctionStartsLoadCommand(true),
-#endif
- fCanReExportSymbols(false), fObjcCategoryMerging(true),
+ fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false),
+ fVersionLoadCommandForcedOff(false), fFunctionStartsLoadCommand(false),
+ fFunctionStartsForcedOn(false), fFunctionStartsForcedOff(false),
+ fCanReExportSymbols(false), fObjcCategoryMerging(true), fPageAlignDataAtoms(false),
fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL),
- fMacVersionMin(ld::macVersionUnset), fIPhoneVersionMin(ld::iPhoneVersionUnset),
+ fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset),
fSaveTempFiles(false)
{
this->checkForClassic(argc, argv);
}
-
-
bool Options::printWhyLive(const char* symbolName) const
{
return ( fWhyLive.find(symbolName) != fWhyLive.end() );
uint32_t Options::maxSegProtection(const char* segName) const
{
// iPhoneOS always uses same protection for max and initial
- if ( fIPhoneVersionMin != ld::iPhoneVersionUnset )
+ if ( fIOSVersionMin != ld::iOSVersionUnset )
return initialSegProtection(segName);
for(std::vector<Options::SegmentProtect>::const_iterator it = fCustomSegmentProtections.begin(); it != fCustomSegmentProtections.end(); ++it) {
throw "internal error";
}
-
void Options::setArchitecture(cpu_type_t type, cpu_subtype_t subtype)
{
fArchitecture = type;
fMakeCompressedDyldInfo = true;
break;
case CPU_TYPE_ARM:
- switch ( subtype ) {
- case CPU_SUBTYPE_ARM_V4T:
- fArchitectureName = "armv4t";
- fHasPreferredSubType = true;
- break;
- case CPU_SUBTYPE_ARM_V5TEJ:
- fArchitectureName = "armv5";
- fHasPreferredSubType = true;
- break;
- case CPU_SUBTYPE_ARM_V6:
- fArchitectureName = "armv6";
- fHasPreferredSubType = true;
- break;
- case CPU_SUBTYPE_ARM_V7:
- fArchitectureName = "armv7";
- fHasPreferredSubType = true;
- break;
- default:
- assert(0 && "unknown arm subtype");
- fArchitectureName = "arm";
- break;
+ fHasPreferredSubType = true;
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( t->subType == subtype ) {
+ fArchitectureName = t->subTypeName;
+ fArchSupportsThumb2 = t->supportsThumb2;
+ break;
+ }
}
- if ( (fMacVersionMin == ld::macVersionUnset) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
+ assert(fArchitectureName != NULL);
+ if ( (fMacVersionMin == ld::macVersionUnset) && (fIOSVersionMin == ld::iOSVersionUnset) && (fOutputKind != Options::kObjectFile) ) {
#if defined(DEFAULT_IPHONEOS_MIN_VERSION)
warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
- setIPhoneVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
+ setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
#elif defined(DEFAULT_MACOSX_MIN_VERSION)
warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION);
setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
fMacVersionMin = ld::mac10_6;
#endif
}
- if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iPhone3_1) && !fMakeCompressedDyldInfoForceOff )
+ if ( !fMakeCompressedDyldInfo && minOS(ld::mac10_6, ld::iOS_3_1) && !fMakeCompressedDyldInfoForceOff )
fMakeCompressedDyldInfo = true;
break;
default:
fSubArchitecture = CPU_SUBTYPE_POWERPC_970;
fHasPreferredSubType = true;
}
- else if ( strcmp(arch, "armv6") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V6;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(arch, "armv5") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V5TEJ;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(arch, "armv4t") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V4T;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(arch, "xscale") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_XSCALE;
- fHasPreferredSubType = true;
- }
- else if ( strcmp(arch, "armv7") == 0 ) {
- fArchitecture = CPU_TYPE_ARM;
- fSubArchitecture = CPU_SUBTYPE_ARM_V7;
- fHasPreferredSubType = true;
- }
- else
+ else {
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( strcmp(t->subTypeName,arch) == 0 ) {
+ fArchitecture = CPU_TYPE_ARM;
+ fSubArchitecture = t->subType;
+ fArchSupportsThumb2 = t->supportsThumb2;
+ fHasPreferredSubType = true;
+ return;
+ }
+ }
throwf("unknown/unsupported architecture name for: -arch %s", arch);
+ }
}
bool Options::checkForFile(const char* format, const char* dir, const char* rootName, FileInfo& result) const
}
}
-void Options::setIPhoneVersionMin(const char* version)
+void Options::setIOSVersionMin(const char* version)
{
if ( version == NULL )
throw "-ios_version_min argument missing";
unsigned int majorVersion = version[0] - '0';
unsigned int minorVersion = version[2] - '0';
- fIPhoneVersionMin = (ld::IPhoneVersionMin)((majorVersion << 16) | (minorVersion << 8));
+ fIOSVersionMin = (ld::IOSVersionMin)((majorVersion << 16) | (minorVersion << 8));
}
-bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IPhoneVersionMin requirediPhoneOSMin)
+bool Options::minOS(ld::MacVersionMin requiredMacMin, ld::IOSVersionMin requirediPhoneOSMin)
{
if ( fMacVersionMin != ld::macVersionUnset ) {
return ( fMacVersionMin >= requiredMacMin );
}
else {
- return ( fIPhoneVersionMin >= requirediPhoneOSMin);
+ return ( fIOSVersionMin >= requirediPhoneOSMin);
}
}
fIgnoreOtherArchFiles = true;
}
else if ( strcmp(arg, "-force_cpusubtype_ALL") == 0 ) {
- fForceSubtypeAll = true;
+ fForceSubtypeAll = true;
+ fAllowCpuSubtypeMismatches = true;
}
// Similar to -weak-l but uses the absolute path name to the library.
else if ( strcmp(arg, "-weak_library") == 0 ) {
else if ( strcmp(arg, "-macosx_version_min") == 0 ) {
setMacOSXVersionMin(argv[++i]);
}
- else if ( (strcmp(arg, "-iphoneos_version_min") == 0) || (strcmp(arg, "-ios_version_min") == 0) ) {
- setIPhoneVersionMin(argv[++i]);
+ else if ( (strcmp(arg, "-ios_version_min") == 0) || (strcmp(arg, "-iphoneos_version_min") == 0) ) {
+ setIOSVersionMin(argv[++i]);
+ }
+ else if ( strcmp(arg, "-ios_simulator_version_min") == 0 ) {
+ setIOSVersionMin(argv[++i]);
}
else if ( strcmp(arg, "-multiply_defined") == 0 ) {
//warnObsolete(arg);
loadSymbolOrderFile(argv[++i], fExportSymbolsOrder);
}
else if ( strcmp(arg, "-no_compact_linkedit") == 0 ) {
- fMakeCompressedDyldInfo = false;
- fMakeCompressedDyldInfoForceOff = true;
+ warnObsolete("-no_compact_linkedit");
}
else if ( strcmp(arg, "-no_eh_labels") == 0 ) {
fNoEHLabels = true;
else if ( strcmp(arg, "-no_zero_fill_sections") == 0 ) {
fOptimizeZeroFill = false;
}
+ else if ( strcmp(arg, "-merge_zero_fill_sections") == 0 ) {
+ fMergeZeroFill = true;
+ }
else if ( strcmp(arg, "-objc_abi_version") == 0 ) {
const char* version = argv[++i];
if ( version == NULL )
fDemangle = true;
}
else if ( strcmp(arg, "-version_load_command") == 0 ) {
- fVersionLoadCommand = true;
+ fVersionLoadCommandForcedOn = true;
+ fVersionLoadCommandForcedOff = false;
}
else if ( strcmp(arg, "-no_version_load_command") == 0 ) {
- fVersionLoadCommand = false;
+ fVersionLoadCommandForcedOff = true;
+ fVersionLoadCommandForcedOn = false;
}
else if ( strcmp(arg, "-function_starts") == 0 ) {
- fFunctionStartsLoadCommand = true;
+ fFunctionStartsForcedOn = true;
+ fFunctionStartsForcedOff = false;
}
else if ( strcmp(arg, "-no_function_starts") == 0 ) {
- fFunctionStartsLoadCommand = false;
+ fFunctionStartsForcedOff = true;
+ fFunctionStartsForcedOn = false;
}
else if ( strcmp(arg, "-object_path_lto") == 0 ) {
fTempLtoObjectPath = argv[++i];
throw "-dyld_env missing ENV=VALUE";
fDyldEnvironExtras.push_back(envarg);
}
+ else if ( strcmp(arg, "-page_align_data_atoms") == 0 ) {
+ fPageAlignDataAtoms = true;
+ }
else {
throwf("unknown option: %s", arg);
}
if (getenv("LD_ALLOW_CPU_SUBTYPE_MISMATCHES") != NULL)
fAllowCpuSubtypeMismatches = true;
- // for now disable compressed linkedit functionality
- if ( getenv("LD_NO_COMPACT_LINKEDIT") != NULL ) {
- fMakeCompressedDyldInfo = false;
- fMakeCompressedDyldInfoForceOff = true;
- }
-
sWarningsSideFilePath = getenv("LD_WARN_FILE");
const char* customDyldPath = getenv("LD_DYLD_PATH");
// set default min OS version
if ( (fMacVersionMin == ld::macVersionUnset)
- && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) {
+ && (fIOSVersionMin == ld::iOSVersionUnset) ) {
// 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 )
setMacOSXVersionMin(macVers);
else if ( iPhoneVers != NULL )
- setIPhoneVersionMin(iPhoneVers);
+ setIOSVersionMin(iPhoneVers);
else if ( iOSVers != NULL )
- setIPhoneVersionMin(iOSVers);
+ setIOSVersionMin(iOSVers);
+ else if ( iOSSimulatorVers != NULL )
+ setIOSVersionMin(iOSSimulatorVers);
else {
// if still nothing, set default based on architecture
switch ( fArchitecture ) {
if ( (fOutputKind != Options::kObjectFile) && (fOutputKind != Options::kPreload) ) {
#if defined(DEFAULT_IPHONEOS_MIN_VERSION)
warning("-ios_version_min not specificed, assuming " DEFAULT_IPHONEOS_MIN_VERSION);
- setIPhoneVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
+ setIOSVersionMin(DEFAULT_IPHONEOS_MIN_VERSION);
#elif defined(DEFAULT_MACOSX_MIN_VERSION)
warning("-macosx_version_min not specificed, assuming " DEFAULT_MACOSX_MIN_VERSION);
setMacOSXVersionMin(DEFAULT_MACOSX_MIN_VERSION);
// adjust min based on architecture
switch ( fArchitecture ) {
case CPU_TYPE_I386:
- if ( (fMacVersionMin < ld::mac10_4) && (fIPhoneVersionMin == ld::iPhoneVersionUnset) ) {
+ if ( (fMacVersionMin < ld::mac10_4) && (fIOSVersionMin == ld::iOSVersionUnset) ) {
//warning("-macosx_version_min should be 10.4 or later for i386");
fMacVersionMin = ld::mac10_4;
}
fAllowTextRelocs = true;
fUndefinedTreatment = kUndefinedDynamicLookup;
break;
+ case CPU_TYPE_ARM:
+ if ( fIOSVersionMin >= ld::iOS_5_0 ) {
+ // iOS 5.0 and later use new MH_KEXT_BUNDLE type
+ fMakeCompressedDyldInfo = false;
+ fMakeCompressedDyldInfoForceOff = true;
+ fAllowTextRelocs = true;
+ fUndefinedTreatment = kUndefinedDynamicLookup;
+ break;
+ }
+ // else use object file
case CPU_TYPE_POWERPC:
case CPU_TYPE_I386:
- case CPU_TYPE_ARM:
// use .o files
fOutputKind = kObjectFile;
break;
// disable implicit dylibs when targeting 10.3
// <rdar://problem/5451987> add option to disable implicit load commands for indirectly used public dylibs
- if ( !minOS(ld::mac10_4, ld::iPhone2_0) )
+ if ( !minOS(ld::mac10_4, ld::iOS_2_0) )
fImplicitlyLinkPublicDylibs = false;
// in 10.5 nothing is prebound
fPrebind = false;
}
+ else if ( fIOSVersionMin != ld::iOSVersionUnset ) {
+ // nothing in simulator is prebound
+ fPrebind = false;
+ }
else {
// in 10.3 and earlier only dylibs and main executables could be prebound
switch ( fOutputKind ) {
// determine if info for shared region should be added
if ( fOutputKind == Options::kDynamicLibrary ) {
- if ( minOS(ld::mac10_5, ld::iPhone3_1) )
+ if ( minOS(ld::mac10_5, ld::iOS_3_1) )
if ( !fPrebind )
if ( (strncmp(this->installPath(), "/usr/lib/", 9) == 0)
|| (strncmp(this->installPath(), "/System/Library/", 16) == 0) )
// figure out if module table is needed for compatibility with old ld/dyld
if ( fOutputKind == Options::kDynamicLibrary ) {
switch ( fArchitecture ) {
+ case CPU_TYPE_I386:
+ if ( fIOSVersionMin != ld::iOSVersionUnset ) // simulator never needs modules
+ break;
case CPU_TYPE_POWERPC: // 10.3 and earlier dyld requires a module table
- case CPU_TYPE_I386: // ld_classic for 10.4.x requires a module table
if ( fMacVersionMin <= ld::mac10_5 )
fNeedsModuleTable = true;
break;
if ( fMakeCompressedDyldInfo ) {
switch (fArchitecture) {
case CPU_TYPE_I386:
+ if ( fIOSVersionMin != ld::iOSVersionUnset ) // simulator always uses compressed LINKEDIT
+ break;
case CPU_TYPE_X86_64:
if ( fMacVersionMin < ld::mac10_6 )
fMakeCompressedDyldInfo = false;
break;
case CPU_TYPE_ARM:
- if ( !minOS(ld::mac10_6, ld::iPhone3_1) )
+ if ( !minOS(ld::mac10_6, ld::iOS_3_1) )
fMakeCompressedDyldInfo = false;
break;
case CPU_TYPE_POWERPC:
}
// Mac OS X 10.5 and iPhoneOS 2.0 support LC_REEXPORT_DYLIB
- if ( minOS(ld::mac10_5, ld::iPhone2_0) )
+ if ( minOS(ld::mac10_5, ld::iOS_2_0) )
fUseSimplifiedDylibReExports = true;
// Mac OS X 10.7 and iOS 4.2 support LC_LOAD_UPWARD_DYLIB
- if ( minOS(ld::mac10_7, ld::iPhone4_2) && (fOutputKind == kDynamicLibrary) )
+ if ( minOS(ld::mac10_7, ld::iOS_4_2) && (fOutputKind == kDynamicLibrary) )
fCanUseUpwardDylib = true;
- // x86_64 for MacOSX 10.7 defaults to PIE
+ // MacOSX 10.7 defaults to PIE
if ( ((fArchitecture == CPU_TYPE_X86_64) || (fArchitecture == CPU_TYPE_I386))
&& (fOutputKind == kDynamicExecutable)
&& (fMacVersionMin >= ld::mac10_7) ) {
// armv7 for iOS4.3 defaults to PIE
if ( (fArchitecture == CPU_TYPE_ARM)
- && (fSubArchitecture == CPU_SUBTYPE_ARM_V7)
+ && fArchSupportsThumb2
&& (fOutputKind == kDynamicExecutable)
- && (fIPhoneVersionMin >= ld::iPhone4_3) ) {
+ && (fIOSVersionMin >= ld::iOS_4_3) ) {
fPositionIndependentExecutable = true;
}
fTLVSupport = true;
}
- // version load command is only in some kinds of output files
+ // default to adding version load command for dynamic code, static code must opt-in
switch ( fOutputKind ) {
case Options::kObjectFile:
+ fVersionLoadCommand = false;
+ break;
case Options::kStaticExecutable:
case Options::kPreload:
case Options::kKextBundle:
- fVersionLoadCommand = false;
- fFunctionStartsLoadCommand = false;
+ if ( fVersionLoadCommandForcedOn )
+ fVersionLoadCommand = true;
break;
case Options::kDynamicExecutable:
case Options::kDyld:
case Options::kDynamicLibrary:
case Options::kDynamicBundle:
+ if ( !fVersionLoadCommandForcedOff )
+ fVersionLoadCommand = true;
+ // <rdar://problem/9945513> for now, don't create version load commands for iOS simulator builds
+ if ( fVersionLoadCommand && (fArchitecture == CPU_TYPE_I386) ) {
+ for (std::vector<const char*>::iterator sdkit = fSDKPaths.begin(); sdkit != fSDKPaths.end(); sdkit++) {
+ if ( strstr(*sdkit, "/iPhoneSimulator.platform/") != NULL )
+ fVersionLoadCommand = false;
+ }
+ }
break;
}
+ // default to adding functions start for dynamic code, static code must opt-in
+ switch ( fOutputKind ) {
+ case Options::kObjectFile:
+ fFunctionStartsLoadCommand = false;
+ break;
+ case Options::kPreload:
+ case Options::kStaticExecutable:
+ case Options::kKextBundle:
+ if ( fFunctionStartsForcedOn )
+ fFunctionStartsLoadCommand = true;
+ break;
+ case Options::kDynamicExecutable:
+ case Options::kDyld:
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ if ( !fFunctionStartsForcedOff )
+ fFunctionStartsLoadCommand = true;
+ break;
+ }
+
// support re-export of individual symbols in MacOSX 10.7 and iOS 4.2
- if ( (fOutputKind == kDynamicLibrary) && minOS(ld::mac10_7, ld::iPhone4_2) )
+ if ( (fOutputKind == kDynamicLibrary) && minOS(ld::mac10_7, ld::iOS_4_2) )
fCanReExportSymbols = true;
// ObjC optimization is only in dynamic final linked images
fInitialUndefines.push_back(*it);
}
- // make sure that -init symbol exist
+ // make sure that -init symbol exists
if ( fInitFunctionName != NULL )
fInitialUndefines.push_back(fInitFunctionName);
+ // make sure that entry symbol exists
+ switch ( fOutputKind ) {
+ case Options::kDynamicExecutable:
+ case Options::kStaticExecutable:
+ case Options::kDyld:
+ case Options::kPreload:
+ fInitialUndefines.push_back(fEntryName);
+ break;
+ case Options::kDynamicLibrary:
+ case Options::kDynamicBundle:
+ case Options::kObjectFile:
+ case Options::kKextBundle:
+ break;
+ }
+
// make sure every alias base exists
for (std::vector<AliasPair>::iterator it=fAliases.begin(); it != fAliases.end(); ++it) {
fInitialUndefines.push_back(it->realName);
// can't use -rpath unless targeting 10.5 or later
if ( fRPaths.size() > 0 ) {
- if ( !minOS(ld::mac10_5, ld::iPhone2_0) )
+ if ( !minOS(ld::mac10_5, ld::iOS_2_0) )
throw "-rpath can only be used when targeting Mac OS X 10.5 or later";
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
if ( fPositionIndependentExecutable ) {
switch ( fOutputKind ) {
case Options::kDynamicExecutable:
- if ( !minOS(ld::mac10_5, ld::iPhone4_2) ) {
- if ( fIPhoneVersionMin == ld::iPhoneVersionUnset )
+ if ( !minOS(ld::mac10_5, ld::iOS_4_2) ) {
+ if ( fIOSVersionMin == ld::iOSVersionUnset )
throw "-pie can only be used when targeting Mac OS X 10.5 or later";
else
throw "-pie can only be used when targeting iOS 4.2 or later";
if ( !fReExportSymbols.empty() ) {
if ( fOutputKind != Options::kDynamicLibrary )
throw "-reexported_symbols_list can only used used when created dynamic libraries";
- if ( !minOS(ld::mac10_7, ld::iPhone4_2) )
+ if ( !minOS(ld::mac10_7, ld::iOS_4_2) )
throw "targeted OS version does not support -reexported_symbols_list";
}
switch ( fArchitecture ) {
case CPU_TYPE_I386:
case CPU_TYPE_POWERPC:
- case CPU_TYPE_ARM:
if ( (staticFound || kextFound) && !newLinker ) {
// this environment variable will disable use of ld_classic for -static links
if ( getenv("LD_NO_CLASSIC_LINKER_STATIC") == NULL ) {
- // ld_classic does not support -iphoneos_version_min, so change
- for(int j=0; j < argc; ++j) {
- if ( (strcmp(argv[j], "-iphoneos_version_min") == 0) || (strcmp(argv[j], "-ios_version_min") == 0) ) {
- argv[j] = "-macosx_version_min";
- if ( j < argc-1 )
- argv[j+1] = "10.5";
- break;
- }
- }
- // ld classic does not understand -kext (change to -static -r)
- if ( kextFound ) {
- for(int j=0; j < argc; ++j) {
- if ( strcmp(argv[j], "-kext") == 0)
- argv[j] = "-r";
- else if ( strcmp(argv[j], "-dynamic") == 0)
- argv[j] = "-static";
- }
- }
- // ld classic does not understand -demangle
- for(int j=0; j < argc; ++j) {
- if ( strcmp(argv[j], "-demangle") == 0)
- argv[j] = "-noprebind";
- }
this->gotoClassicLinker(argc, argv);
}
}
void Options::gotoClassicLinker(int argc, const char* argv[])
{
+ warning("using ld_classic");
argv[0] = "ld_classic";
+ // ld_classic does not support -iphoneos_version_min, so change
+ for(int j=0; j < argc; ++j) {
+ if ( (strcmp(argv[j], "-iphoneos_version_min") == 0) || (strcmp(argv[j], "-ios_version_min") == 0) ) {
+ argv[j] = "-macosx_version_min";
+ if ( j < argc-1 )
+ argv[j+1] = "10.5";
+ break;
+ }
+ }
+ // ld classic does not understand -kext (change to -static -r)
+ for(int j=0; j < argc; ++j) {
+ if ( strcmp(argv[j], "-kext") == 0)
+ argv[j] = "-r";
+ else if ( strcmp(argv[j], "-dynamic") == 0)
+ argv[j] = "-static";
+ }
+ // ld classic does not understand -demangle
+ for(int j=0; j < argc; ++j) {
+ if ( strcmp(argv[j], "-demangle") == 0)
+ argv[j] = "-noprebind";
+ }
// in -v mode, print command line passed to ld_classic
for(int i=0; i < argc; ++i) {
if ( strcmp(argv[i], "-v") == 0 ) {
bool forceCpuSubtypeAll() const { return fForceSubtypeAll; }
const char* architectureName() const { return fArchitectureName; }
void setArchitecture(cpu_type_t, cpu_subtype_t subtype);
+ bool archSupportsThumb2() const { return fArchSupportsThumb2; }
OutputKind outputKind() const { return fOutputKind; }
bool prebind() const { return fPrebind; }
bool bindAtLoad() const { return fBindAtLoad; }
bool deadCodeStrip() const { return fDeadStrip; }
UndefinedTreatment undefinedTreatment() const { return fUndefinedTreatment; }
ld::MacVersionMin macosxVersionMin() const { return fMacVersionMin; }
- ld::IPhoneVersionMin iphoneOSVersionMin() const { return fIPhoneVersionMin; }
- bool minOS(ld::MacVersionMin mac, ld::IPhoneVersionMin iPhoneOS);
+ ld::IOSVersionMin iOSVersionMin() const { return fIOSVersionMin; }
+ bool minOS(ld::MacVersionMin mac, ld::IOSVersionMin iPhoneOS);
bool messagesPrefixedWithArchitecture();
Treatment picTreatment();
WeakReferenceMismatchTreatment weakReferenceMismatchTreatment() const { return fWeakReferenceMismatchTreatment; }
bool loadAllObjcObjectsFromArchives() const { return fLoadAllObjcObjectsFromArchives; }
bool autoOrderInitializers() const { return fAutoOrderInitializers; }
bool optimizeZeroFill() const { return fOptimizeZeroFill; }
+ bool mergeZeroFill() const { return fMergeZeroFill; }
bool logAllFiles() const { return fLogAllFiles; }
DebugInfoStripping debugInfoStripping() const { return fDebugInfoStripping; }
bool flatNamespace() const { return fFlatNamespace; }
bool canReExportSymbols() const { return fCanReExportSymbols; }
const char* tempLtoObjectPath() const { return fTempLtoObjectPath; }
bool objcCategoryMerging() const { return fObjcCategoryMerging; }
+ bool pageAlignDataAtoms() const { return fPageAlignDataAtoms; }
bool hasWeakBitTweaks() const;
bool forceWeak(const char* symbolName) const;
bool forceNotWeak(const char* symbolName) const;
void parsePostCommandLineEnvironmentSettings();
void setUndefinedTreatment(const char* treatment);
void setMacOSXVersionMin(const char* version);
- void setIPhoneVersionMin(const char* version);
+ void setIOSVersionMin(const char* version);
void setWeakReferenceMismatchTreatment(const char* treatment);
void addDylibOverride(const char* paths);
void addSectionAlignment(const char* segment, const char* section, const char* alignment);
const char* fArchitectureName;
OutputKind fOutputKind;
bool fHasPreferredSubType;
+ bool fArchSupportsThumb2;
bool fPrebind;
bool fBindAtLoad;
bool fKeepPrivateExterns;
bool fRemoveDwarfUnwindIfCompactExists;
bool fAutoOrderInitializers;
bool fOptimizeZeroFill;
+ bool fMergeZeroFill;
bool fLogObjectFiles;
bool fLogAllFiles;
bool fTraceDylibs;
bool fDemangle;
bool fTLVSupport;
bool fVersionLoadCommand;
+ bool fVersionLoadCommandForcedOn;
+ bool fVersionLoadCommandForcedOff;
bool fFunctionStartsLoadCommand;
+ bool fFunctionStartsForcedOn;
+ bool fFunctionStartsForcedOff;
bool fCanReExportSymbols;
bool fObjcCategoryMerging;
+ bool fPageAlignDataAtoms;
DebugInfoStripping fDebugInfoStripping;
const char* fTraceOutputFile;
ld::MacVersionMin fMacVersionMin;
- ld::IPhoneVersionMin fIPhoneVersionMin;
+ ld::IOSVersionMin fIOSVersionMin;
std::vector<AliasPair> fAliases;
std::vector<const char*> fInitialUndefines;
NameSet fAllowedUndefined;
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
*
- * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
uint64_t offset = 0;
for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
- if ( atom->alignment().powerOf2 > maxAlignment )
- maxAlignment = atom->alignment().powerOf2;
+ bool pagePerAtom = false;
+ uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2;
+ if ( _options.pageAlignDataAtoms() && ( strcmp(atom->section().segmentName(), "__DATA") == 0) ) {
+ switch ( atom->section().type() ) {
+ case ld::Section::typeUnclassified:
+ case ld::Section::typeTentativeDefs:
+ case ld::Section::typeZeroFill:
+ pagePerAtom = true;
+ if ( atomAlignmentPowerOf2 < 12 )
+ atomAlignmentPowerOf2 = 12;
+ break;
+ default:
+ break;
+ }
+ }
+ if ( atomAlignmentPowerOf2 > maxAlignment )
+ maxAlignment = atomAlignmentPowerOf2;
// calculate section offset for this atom
- uint64_t alignment = 1 << atom->alignment().powerOf2;
+ uint64_t alignment = 1 << atomAlignmentPowerOf2;
uint64_t currentModulus = (offset % alignment);
uint64_t requiredModulus = atom->alignment().modulus;
if ( currentModulus != requiredModulus ) {
if ( sect->type() != ld::Section::typeLinkEdit ) {
(const_cast<ld::Atom*>(atom))->setSectionOffset(offset);
offset += atom->size();
+ if ( pagePerAtom ) {
+ offset = (offset + 4095) & (-4096); // round up to end of page
+ }
}
if ( (atom->scope() == ld::Atom::scopeGlobal)
&& (atom->definition() == ld::Atom::definitionRegular)
for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) {
ld::Internal::FinalSection* sect = *it;
if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) {
- _encryptedTEXTendOffset = pageAlign(sect->fileOffset);
+ _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size);
}
}
}
}
}
+
+void OutputFile::rangeCheckAbsolute32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup)
+{
+ const int64_t fourGigLimit = 0xFFFFFFFF;
+ if ( displacement > fourGigLimit ) {
+ // <rdar://problem/9610466> cannot enforce 32-bit range checks on 32-bit archs because assembler loses sign information
+ // .long _foo - 0xC0000000
+ // is encoded in mach-o the same as:
+ // .long _foo + 0x40000000
+ // so if _foo lays out to 0xC0000100, the first is ok, but the second is not.
+ if ( (_options.architecture() == CPU_TYPE_ARM) || (_options.architecture() == CPU_TYPE_I386) ) {
+ // Unlikely userland code does funky stuff like this, so warn for them, but not warn for -preload
+ if ( _options.outputKind() != Options::kPreload ) {
+ warning("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to 0x%08llX",
+ displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), displacement);
+ }
+ return;
+ }
+ // show layout of final image
+ printSectionLayout(state);
+
+ const ld::Atom* target;
+ if ( fixup->binding == ld::Fixup::bindingNone )
+ throwf("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to 0x%08llX",
+ displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), displacement);
+ else
+ throwf("32-bit absolute address out of range (0x%08llX max is 4GB): from %s + 0x%08X (0x%08llX) to %s (0x%08llX)",
+ displacement, atom->name(), fixup->offsetInAtom, atom->finalAddress(), referenceTargetAtomName(state, fixup),
+ addressOf(state, fixup, &target));
+ }
+}
+
+
void OutputFile::rangeCheckRIP32(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup)
{
const int64_t twoGigLimit = 0x7FFFFFFF;
- if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) {
+ if ( (displacement > twoGigLimit) || (displacement < (-twoGigLimit)) ) {
// show layout of final image
printSectionLayout(state);
void OutputFile::rangeCheckThumbBranch22(int64_t displacement, ld::Internal& state, const ld::Atom* atom, const ld::Fixup* fixup)
{
- // armv7 supports a larger displacement
- if ( _options.preferSubArchitecture() && (_options.subArchitecture() == CPU_SUBTYPE_ARM_V7) ) {
+ // thumb2 supports a larger displacement
+ if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) {
if ( (displacement > 16777214LL) || (displacement < (-16777216LL)) ) {
// show layout of final image
printSectionLayout(state);
set32LE(fixUpLocation, (get32LE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) );
break;
case ld::Fixup::kindStoreLittleEndian32:
+ rangeCheckAbsolute32(accumulator, state, atom, fit);
set32LE(fixUpLocation, accumulator);
break;
case ld::Fixup::kindStoreLittleEndian64:
set32BE(fixUpLocation, (get32BE(fixUpLocation) & 0xFF000000) | (accumulator & 0x00FFFFFF) );
break;
case ld::Fixup::kindStoreBigEndian32:
+ rangeCheckAbsolute32(accumulator, state, atom, fit);
set32BE(fixUpLocation, accumulator);
break;
case ld::Fixup::kindStoreBigEndian64:
accumulator |= 1;
if ( fit->contentAddendOnly )
accumulator = 0;
+ rangeCheckAbsolute32(accumulator, state, atom, fit);
set32LE(fixUpLocation, accumulator);
break;
case ld::Fixup::kindStoreTargetAddressLittleEndian64:
// Make sure we are calling arm with bl, thumb with blx
is_bl = ((instruction & 0xFF000000) == 0xEB000000);
is_blx = ((instruction & 0xFE000000) == 0xFA000000);
+ is_b = !is_blx && ((instruction & 0x0F000000) == 0x0A000000);
if ( is_bl && thumbTarget ) {
uint32_t opcode = 0xFA000000;
uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF;
uint32_t disp = (uint32_t)(delta >> 2) & 0x00FFFFFF;
newInstruction = opcode | disp;
}
+ else if ( is_b && thumbTarget ) {
+ if ( fit->contentDetlaToAddendOnly )
+ newInstruction = (instruction & 0xFF000000) | ((uint32_t)(delta >> 2) & 0x00FFFFFF);
+ else
+ throwf("no pc-rel bx arm instruction. Can't fix up branch to %s in %s",
+ referenceTargetAtomName(state, fit), atom->name());
+ }
else if ( !is_bl && !is_blx && thumbTarget ) {
throwf("don't know how to convert instruction %x referencing %s to thumb",
instruction, referenceTargetAtomName(state, fit));
// Since blx cannot have the low bit set, set bit[1] of the target to
// bit[1] of the base address, so that the difference is a multiple of
// 4 bytes.
- if ( !thumbTarget ) {
+ if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) {
accumulator &= -3ULL;
accumulator |= ((atom->finalAddress() + fit->offsetInAtom ) & 2LL);
}
// The pc added will be +4 from the pc
delta = accumulator - (atom->finalAddress() + fit->offsetInAtom + 4);
rangeCheckThumbBranch22(delta, state, atom, fit);
- if ( _options.preferSubArchitecture() && _options.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) {
+ if ( _options.preferSubArchitecture() && _options.archSupportsThumb2() ) {
// The instruction is really two instructions:
// The lower 16 bits are the first instruction, which contains the high
// 11 bits of the displacement.
instruction = 0xC000F000; // keep blx
}
else if ( is_b ) {
- if ( !thumbTarget )
- throwf("don't know how to convert instruction %x referencing %s to arm",
- instruction, referenceTargetAtomName(state, fit));
instruction = 0x9000F000; // keep b
+ if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) {
+ throwf("armv7 has no pc-rel bx thumb instruction. Can't fix up branch to %s in %s",
+ referenceTargetAtomName(state, fit), atom->name());
+ }
}
- else if ( is_b ) {
+ else {
if ( !thumbTarget )
throwf("don't know how to convert branch instruction %x referencing %s to bx",
instruction, referenceTargetAtomName(state, fit));
else if ( is_blx && thumbTarget ) {
instruction = 0xF800F000;
}
- else if ( !is_bl && !is_blx && !thumbTarget ) {
- throwf("don't know how to convert instruction %x referencing %s to arm",
- instruction, referenceTargetAtomName(state, fit));
- }
+ else if ( is_b ) {
+ instruction = 0x9000F000; // keep b
+ if ( !thumbTarget && !fit->contentDetlaToAddendOnly ) {
+ throwf("armv6 has no pc-rel bx thumb instruction. Can't fix up branch to %s in %s",
+ referenceTargetAtomName(state, fit), atom->name());
+ }
+ }
else {
instruction = instruction & 0xF800F800;
}
}
}
-void OutputFile::copyNoOps(uint8_t* from, uint8_t* to)
+void OutputFile::copyNoOps(uint8_t* from, uint8_t* to, bool thumb)
{
switch ( _options.architecture() ) {
case CPU_TYPE_POWERPC:
*p = 0x90;
break;
case CPU_TYPE_ARM:
- // fixme: need thumb nop?
- for (uint8_t* p=from; p < to; p += 4)
- OSWriteBigInt32((uint32_t*)p, 0, 0xe1a00000);
+ if ( thumb ) {
+ for (uint8_t* p=from; p < to; p += 2)
+ OSWriteLittleInt16((uint16_t*)p, 0, 0x46c0);
+ }
+ else {
+ for (uint8_t* p=from; p < to; p += 4)
+ OSWriteLittleInt32((uint32_t*)p, 0, 0xe1a00000);
+ }
break;
default:
for (uint8_t* p=from; p < to; ++p)
const bool sectionUsesNops = (sect->type() == ld::Section::typeCode);
//fprintf(stderr, "file offset=0x%08llX, section %s\n", sect->fileOffset, sect->sectionName());
std::vector<const ld::Atom*>& atoms = sect->atoms;
+ bool lastAtomWasThumb = false;
for (std::vector<const ld::Atom*>::iterator ait = atoms.begin(); ait != atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
if ( atom->definition() == ld::Atom::definitionProxy )
uint64_t fileOffset = atom->finalAddress() - sect->address + sect->fileOffset;
// check for alignment padding between atoms
if ( (fileOffset != fileOffsetOfEndOfLastAtom) && lastAtomUsesNoOps ) {
- this->copyNoOps(&wholeBuffer[fileOffsetOfEndOfLastAtom], &wholeBuffer[fileOffset]);
+ this->copyNoOps(&wholeBuffer[fileOffsetOfEndOfLastAtom], &wholeBuffer[fileOffset], lastAtomWasThumb);
}
// copy atom content
atom->copyRawContent(&wholeBuffer[fileOffset]);
this->applyFixUps(state, mhAddress, atom, &wholeBuffer[fileOffset]);
fileOffsetOfEndOfLastAtom = fileOffset+atom->size();
lastAtomUsesNoOps = sectionUsesNops;
+ lastAtomWasThumb = atom->isThumb();
}
catch (const char* msg) {
if ( atom->file() != NULL )
}
};
+class NotInSet
+{
+public:
+ NotInSet(const std::set<const ld::Atom*>& theSet) : _set(theSet) {}
+
+ bool operator()(const ld::Atom* atom) const {
+ return ( _set.count(atom) == 0 );
+ }
+private:
+ const std::set<const ld::Atom*>& _set;
+};
+
+
void OutputFile::buildSymbolTable(ld::Internal& state)
{
unsigned int machoSectionIndex = 0;
}
}
else {
- if ( _options.keepLocalSymbol(atom->name()) )
+ if ( _options.keepLocalSymbol(atom->name()) )
+ _localAtoms.push_back(atom);
+ // <rdar://problem/5804214> ld should never have a symbol in the non-lazy indirect symbol table with index 0
+ // this works by making __mh_execute_header be a local symbol which takes symbol index 0
+ else if ( (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip) && !_options.makeCompressedDyldInfo() )
_localAtoms.push_back(atom);
else
(const_cast<ld::Atom*>(atom))->setSymbolTableInclusion(ld::Atom::symbolTableNotIn);
}
}
+ // <rdar://problem/6978069> ld adds undefined symbol from .exp file to binary
+ if ( (_options.outputKind() == Options::kKextBundle) && _options.hasExportRestrictList() ) {
+ // search for referenced undefines
+ std::set<const ld::Atom*> referencedProxyAtoms;
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ switch ( fit->binding ) {
+ case ld::Fixup::bindingsIndirectlyBound:
+ referencedProxyAtoms.insert(state.indirectBindingTable[fit->u.bindingIndex]);
+ break;
+ case ld::Fixup::bindingDirectlyBound:
+ referencedProxyAtoms.insert(fit->u.target);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ // remove any unreferenced _importedAtoms
+ _importedAtoms.erase(std::remove_if(_importedAtoms.begin(), _importedAtoms.end(), NotInSet(referencedProxyAtoms)), _importedAtoms.end());
+ }
+
// sort by name
std::sort(_exportedAtoms.begin(), _exportedAtoms.end(), AtomByNameSorter());
std::sort(_importedAtoms.begin(), _importedAtoms.end(), AtomByNameSorter());
-
}
void OutputFile::addPreloadLinkEdit(ld::Internal& state)
if ( _options.warnAboutTextRelocs() )
warning("text reloc in %s to %s", atom->name(), target->name());
}
- else if ( _options.positionIndependentExecutable() && ((_options.iphoneOSVersionMin() >= ld::iPhone4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) {
+ else if ( _options.positionIndependentExecutable() && ((_options.iOSVersionMin() >= ld::iOS_4_3) || (_options.macosxVersionMin() >= ld::mac10_7)) ) {
if ( ! this->pieDisabled ) {
warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, "
"but used in %s from %s. "
}
this->pieDisabled = true;
}
+ else if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) ) {
+ throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path());
+ }
else {
- throwf("illegal text reloc to %s from %s in %s", target->name(), target->file()->path(), atom->name());
+ throwf("illegal text-relocation to %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path());
}
}
// no need to rebase or bind PCRel stores
if ( this->isPcRelStore(fixupWithStore->kind) ) {
// as long as target is in same linkage unit
- if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) )
+ if ( (target == NULL) || (target->definition() != ld::Atom::definitionProxy) ) {
+ // make sure target is not global and weak
+ if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular)) {
+ if ( (atom->section().type() == ld::Section::typeCFI)
+ || (atom->section().type() == ld::Section::typeDtraceDOF)
+ || (atom->section().type() == ld::Section::typeUnwindInfo) ) {
+ // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols
+ return;
+ }
+ // Have direct reference to weak-global. This should be an indrect reference
+ warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. "
+ "This was likely caused by different translation units being compiled with different visibility settings.",
+ atom->name(), target->name());
+ }
return;
+ }
}
// no need to rebase or bind PIC internal pointer diff
return;
}
- // make sure target is not global and weak
- if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName)
- && (atom->section().type() != ld::Section::typeCFI)
- && (atom->section().type() != ld::Section::typeDtraceDOF)
- && (atom->section().type() != ld::Section::typeUnwindInfo) ) {
- // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols
- throwf("bad codegen, pointer diff in %s to global weak symbol %s", atom->name(), target->name());
+ // check if target of pointer-diff is global and weak
+ if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) {
+ if ( (atom->section().type() == ld::Section::typeCFI)
+ || (atom->section().type() == ld::Section::typeDtraceDOF)
+ || (atom->section().type() == ld::Section::typeUnwindInfo) ) {
+ // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols
+ return;
+ }
+ // Have direct reference to weak-global. This should be an indrect reference
+ warning("direct access in %s to global weak symbol %s means the weak symbol cannot be overridden at runtime. "
+ "This was likely caused by different translation units being compiled with different visibility settings.",
+ atom->name(), target->name());
}
return;
}
uint8_t rebaseType = REBASE_TYPE_POINTER;
uint8_t type = BIND_TYPE_POINTER;
const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file());
- bool weak_import = ((dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()));
+ bool weak_import = ((dylib != NULL) && (fixupWithTarget->weakImport || dylib->forcedWeakLinked()));
uint64_t address = atom->finalAddress() + fixupWithTarget->offsetInAtom;
uint64_t addend = targetAddend - minusTargetAddend;
}
if ( needsWeakBinding )
_weakBindingInfo.push_back(BindingInfo(type, 0, target->name(), false, address, addend));
-
- // record if weak imported
- if ( weak_import && (target->definition() == ld::Atom::definitionProxy) )
- (const_cast<ld::Atom*>(target))->setWeakImported();
}
if ( (sect->type() == ld::Section::typeNonLazyPointer) && (_options.outputKind() != Options::kKextBundle) ) {
assert(target != NULL);
assert(fixupWithTarget != NULL);
- // record if weak imported
- const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file());
- if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) )
- (const_cast<ld::Atom*>(target))->setWeakImported();
return;
}
switch ( fixupWithStore->kind ) {
case ld::Fixup::kindLazyTarget:
- {
- // lazy pointers don't need relocs, but might need weak_import bit set
- const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(target->file());
- if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) )
- (const_cast<ld::Atom*>(target))->setWeakImported();
- }
+ // lazy pointers don't need relocs
break;
case ld::Fixup::kindStoreLittleEndian32:
case ld::Fixup::kindStoreLittleEndian64:
_externalRelocsAtom->addExternalPointerReloc(relocAddress, target);
sect->hasExternalRelocs = true;
fixupWithTarget->contentAddendOnly = true;
- // record if weak imported
- if ( (dylib != NULL) && (fixupWithTarget->weakImport || dylib->willBeWeakLinked()) )
- (const_cast<ld::Atom*>(target))->setWeakImported();
}
else if ( needsLocalReloc ) {
assert(target != NULL);
}
}
break;
+
+ case ld::Fixup::kindStoreARMLow16:
+ case ld::Fixup::kindStoreThumbLow16:
+ // no way to encode rebasing of binding for these instructions
+ if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) )
+ throwf("no supported runtime lo16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name());
+ break;
+
+ case ld::Fixup::kindStoreARMHigh16:
+ case ld::Fixup::kindStoreThumbHigh16:
+ // no way to encode rebasing of binding for these instructions
+ if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) )
+ throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name());
+ break;
+
default:
break;
}
// x86_64 uses external relocations for everthing that has a symbol
return ( target->symbolTableInclusion() != ld::Atom::symbolTableNotIn );
}
+
+ // <rdar://problem/9513487> support arm branch interworking in -r mode
+ if ( (_options.architecture() == CPU_TYPE_ARM) && (_options.outputKind() == Options::kObjectFile) ) {
+ if ( atom->isThumb() != target->isThumb() ) {
+ switch ( fixupWithTarget->kind ) {
+ // have branch that switches mode, then might be 'b' not 'bl'
+ // Force external relocation, since no way to do local reloc for 'b'
+ case ld::Fixup::kindStoreTargetAddressThumbBranch22 :
+ case ld::Fixup::kindStoreTargetAddressARMBranch24:
+ return true;
+ default:
+ break;
+ }
+ }
+ }
+
// most architectures use external relocations only for references
// to a symbol in another translation unit or for references to "weak symbols" or tentative definitions
assert(target != NULL);
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;
+ uint64_t accumulator = 0;
+ bool thumbTarget;
bool hadSubtract = false;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
if ( fit->firstInCluster() )
target = NULL;
- if ( fit->kind == ld::Fixup::kindSubtractTargetAddress ) {
- hadSubtract = true;
- continue;
+ if ( this->setsTarget(fit->kind) ) {
+ accumulator = addressOf(state, fit, &target);
+ thumbTarget = targetIsThumb(state, fit);
+ if ( thumbTarget )
+ accumulator |= 1;
}
- switch ( fit->binding ) {
- case ld::Fixup::bindingNone:
- case ld::Fixup::bindingByNameUnbound:
+ switch ( fit->kind ) {
+ case ld::Fixup::kindSubtractTargetAddress:
+ accumulator -= addressOf(state, fit, &fromTarget);
+ hadSubtract = true;
break;
- case ld::Fixup::bindingByContentBound:
- case ld::Fixup::bindingDirectlyBound:
- target = fit->u.target;
+ case ld::Fixup::kindAddAddend:
+ accumulator += fit->u.addend;
break;
- case ld::Fixup::bindingsIndirectlyBound:
- target = state.indirectBindingTable[fit->u.bindingIndex];
+ case ld::Fixup::kindSubtractAddend:
+ accumulator -= fit->u.addend;
break;
- }
- switch ( fit->kind ) {
case ld::Fixup::kindStoreBigEndian32:
case ld::Fixup::kindStoreLittleEndian32:
case ld::Fixup::kindStoreLittleEndian64:
// there is also a text reloc which update_dyld_shared_cache will use.
if ( ! hadSubtract )
break;
+ // fall through
case ld::Fixup::kindStoreX86PCRel32:
case ld::Fixup::kindStoreX86PCRel32_1:
case ld::Fixup::kindStoreX86PCRel32_2:
case ld::Fixup::kindStoreTargetAddressX86PCRel32:
case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA:
+ case ld::Fixup::kindStoreARMLow16:
+ case ld::Fixup::kindStoreThumbLow16:
assert(target != NULL);
if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) {
_splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind));
}
break;
+ case ld::Fixup::kindStoreARMHigh16:
+ case ld::Fixup::kindStoreThumbHigh16:
+ assert(target != NULL);
+ if ( strcmp(sect->segmentName(), target->section().segmentName()) != 0 ) {
+ // hi16 needs to know upper 4-bits of low16 to compute carry
+ uint32_t extra = (accumulator >> 12) & 0xF;
+ _splitSegInfos.push_back(SplitSegInfoEntry(atom->finalAddress()+fit->offsetInAtom,fit->kind, extra));
+ }
+ break;
+ case ld::Fixup::kindSetTargetImageOffset:
+ accumulator = addressOf(state, fit, &target);
+ assert(target != NULL);
+ hadSubtract = true;
+ break;
default:
break;
}
return (leftFileOrdinal < rightFileOrdinal);
// then sort by atom objectAddress
- uint64_t leftAddr = left->objectAddress();
- uint64_t rightAddr = right->objectAddress();
+ uint64_t leftAddr = left->finalAddress();
+ uint64_t rightAddr = right->finalAddress();
return leftAddr < rightAddr;
}
};
const ld::relocatable::File* atomObjFile = dynamic_cast<const ld::relocatable::File*>(atomFile);
const char* newDirPath;
const char* newFilename;
- //fprintf(stderr, "debug note for %s\n", atom->getDisplayName());
+ //fprintf(stderr, "debug note for %s\n", atom->name());
if ( atom->translationUnitSource(&newDirPath, &newFilename) ) {
// need SO's whenever the translation unit source file changes
if ( newFilename != filename ) {
// add the source file path to seenFiles so it does not show up in SOLs
seenFiles.insert(newFilename);
char* fullFilePath;
- asprintf(&fullFilePath, "%s/%s", newDirPath, newFilename);
+ asprintf(&fullFilePath, "%s%s", newDirPath, newFilename);
// add both leaf path and full path
seenFiles.insert(fullFilePath);
}
};
struct SplitSegInfoEntry {
- SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k) : address(a), kind(k) {}
+ SplitSegInfoEntry(uint64_t a, ld::Fixup::Kind k, uint32_t e=0) : address(a), kind(k), extra(e) {}
uint64_t address;
ld::Fixup::Kind kind;
+ uint32_t extra;
};
private:
uint64_t addressOf(const ld::Internal& state, const ld::Fixup* fixup, const ld::Atom** target);
bool targetIsThumb(ld::Internal& state, const ld::Fixup* fixup);
uint32_t lazyBindingInfoOffsetForLazyPointerAddress(uint64_t lpAddress);
- void copyNoOps(uint8_t* from, uint8_t* to);
+ void copyNoOps(uint8_t* from, uint8_t* to, bool thumb);
bool isPointerToTarget(ld::Fixup::Kind kind);
bool isPointerFromTarget(ld::Fixup::Kind kind);
bool isPcRelStore(ld::Fixup::Kind kind);
const ld::Fixup* fixup);
void rangeCheckBranch32(int64_t delta, ld::Internal& state, const ld::Atom* atom,
const ld::Fixup* fixup);
+ void rangeCheckAbsolute32(int64_t delta, ld::Internal& state, const ld::Atom* atom,
+ const ld::Fixup* fixup);
void rangeCheckRIP32(int64_t delta, ld::Internal& state, const ld::Atom* atom,
const ld::Fixup* fixup);
void rangeCheckARM12(int64_t delta, ld::Internal& state, const ld::Atom* atom,
else if ( nextObjectSubType == CPU_SUBTYPE_ARM_ALL ) {
warning("CPU_SUBTYPE_ARM_ALL subtype is deprecated: %s", file.path());
}
+ else if ( _options.allowSubArchitectureMismatches() ) {
+ //warning("object file %s was built for different arm sub-type (%d) than link command line (%d)",
+ // file.path(), nextObjectSubType, _options.subArchitecture());
+ }
else {
throwf("object file %s was built for different arm sub-type (%d) than link command line (%d)",
file.path(), nextObjectSubType, _options.subArchitecture());
// marking proxy atom as global triggers the re-export
(const_cast<ld::Atom*>(&atom))->setScope(ld::Atom::scopeGlobal);
}
- else {
+ else if ( _options.outputKind() == Options::kDynamicLibrary ) {
if ( atom.file() != NULL )
- warning("cannot re-export symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path());
+ warning("target OS does not support re-exporting symbol %s from %s\n", SymbolTable::demangle(name), atom.file()->path());
else
- warning("cannot re-export symbol %s\n", SymbolTable::demangle(name));
+ warning("target OS does not support re-exporting symbol %s\n", SymbolTable::demangle(name));
}
}
else {
const char* undef = *it;
// load for previous undefine may also have loaded this undefine, so check again
if ( ! _symbolTable.hasName(undef) ) {
- _inputFiles.searchLibraries(undef, true, true, *this);
+ _inputFiles.searchLibraries(undef, true, true, false, *this);
if ( !_symbolTable.hasName(undef) && (_options.outputKind() != Options::kObjectFile) ) {
if ( strncmp(undef, "section$", 8) == 0 ) {
if ( strncmp(undef, "section$start$", 14) == 0 ) {
const ld::Atom* curAtom = _symbolTable.atomForSlot(_symbolTable.findSlotForName(*it));
assert(curAtom != NULL);
if ( curAtom->definition() == ld::Atom::definitionTentative ) {
- _inputFiles.searchLibraries(*it, searchDylibs, true, *this);
+ _inputFiles.searchLibraries(*it, searchDylibs, true, true, *this);
}
}
}
case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad:
+ case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA:
+ case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad:
+ case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA:
case ld::Fixup::kindStoreTargetAddressARMBranch24:
case ld::Fixup::kindStoreTargetAddressThumbBranch22:
case ld::Fixup::kindStoreTargetAddressPPCBranch24:
target = _internal.indirectBindingTable[fit->u.bindingIndex];
if ( target == NULL ) {
const char* targetName = _symbolTable.indirectName(fit->u.bindingIndex);
- _inputFiles.searchLibraries(targetName, true, true, *this);
+ _inputFiles.searchLibraries(targetName, true, true, false, *this);
target = _internal.indirectBindingTable[fit->u.bindingIndex];
}
if ( target != NULL ) {
if ( target->definition() == ld::Atom::definitionTentative ) {
// <rdar://problem/5894163> need to search archives for overrides of common symbols
bool searchDylibs = (_options.commonsMode() == Options::kCommonsOverriddenByDylibs);
- _inputFiles.searchLibraries(target->name(), searchDylibs, true, *this);
+ _inputFiles.searchLibraries(target->name(), searchDylibs, true, true, *this);
// recompute target since it may have been overridden by searchLibraries()
target = _internal.indirectBindingTable[fit->u.bindingIndex];
}
}
+class NotLiveLTO {
+public:
+ bool operator()(const ld::Atom* atom) const {
+ if (atom->live() || atom->dontDeadStrip() )
+ return false;
+ // don't kill combinable atoms in first pass
+ switch ( atom->combine() ) {
+ case ld::Atom::combineByNameAndContent:
+ case ld::Atom::combineByNameAndReferences:
+ return false;
+ default:
+ return true;
+ }
+ }
+};
void Resolver::deadStripOptimize()
{
for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) {
SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(*uit);
if ( _internal.indirectBindingTable[slot] == NULL ) {
- _inputFiles.searchLibraries(*uit, false, true, *this);
+ _inputFiles.searchLibraries(*uit, false, true, false, *this);
}
if ( _internal.indirectBindingTable[slot] != NULL )
_deadStripRoots.insert(_internal.indirectBindingTable[slot]);
fprintf(stderr, " live=%d name=%s\n", (*it)->live(), (*it)->name());
}
}
- _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end());
+
+ if ( _haveLLVMObjs ) {
+ // <rdar://problem/9777977> don't remove combinable atoms, they may come back in lto output
+ _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLiveLTO()), _atoms.end());
+ }
+ else {
+ _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), NotLive()), _atoms.end());
+ }
}
// search all live atoms for references that are unbound
for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
const ld::Atom* atom = *it;
- assert(atom->live());
+ if ( ! atom->live() )
+ continue;
for (ld::Fixup::iterator fit=atom->fixupsBegin(); fit != atom->fixupsEnd(); ++fit) {
switch ( (ld::Fixup::TargetBinding)fit->binding ) {
case ld::Fixup::bindingByNameUnbound:
bool printedStart = false;
for (SymbolTable::byNameIterator sit=_symbolTable.begin(); sit != _symbolTable.end(); sit++) {
const ld::Atom* atom = *sit;
- if ( (atom != NULL) && (strstr(atom->name(), name) != NULL) ) {
+ if ( (atom != NULL) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) && (strstr(atom->name(), name) != NULL) ) {
if ( ! printedStart ) {
fprintf(stderr, " (maybe you meant: %s", atom->name());
printedStart = true;
}
if ( printedStart )
fprintf(stderr, ")\n");
+ // <rdar://problem/8989530> Add comment to error message when __ZTV symbols are undefined
+ if ( strncmp(name, "__ZTV", 5) == 0 ) {
+ fprintf(stderr, " NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.\n");
+ }
}
}
if ( doError )
// <rdar://problem/5048861> No warning about tentative definition conflicting with dylib definition
// for each tentative definition in symbol table look for dylib that exports same symbol name
if ( atom->definition() == ld::Atom::definitionTentative ) {
- _inputFiles.searchLibraries(atom->name(), true, false, *this);
+ _inputFiles.searchLibraries(atom->name(), true, false, false, *this);
}
// record any overrides of weak symbols in any linked dylib
if ( (atom->definition() == ld::Atom::definitionRegular) && (atom->symbolTableInclusion() == ld::Atom::symbolTableIn) ) {
const ld::Atom* Resolver::entryPoint(bool searchArchives)
{
const char* symbolName = NULL;
+ bool makingDylib = false;
switch ( _options.outputKind() ) {
case Options::kDynamicExecutable:
case Options::kStaticExecutable:
break;
case Options::kDynamicLibrary:
symbolName = _options.initFunctionName();
+ makingDylib = true;
break;
case Options::kObjectFile:
case Options::kDynamicBundle:
SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(symbolName);
if ( (_internal.indirectBindingTable[slot] == NULL) && searchArchives ) {
// <rdar://problem/7043256> ld64 can not find a -e entry point from an archive
- _inputFiles.searchLibraries(symbolName, false, true, *this);
+ _inputFiles.searchLibraries(symbolName, false, true, false, *this);
}
if ( _internal.indirectBindingTable[slot] == NULL ) {
if ( strcmp(symbolName, "start") == 0 )
else
throwf("entry point (%s) undefined.", symbolName);
}
+ else if ( _internal.indirectBindingTable[slot]->definition() == ld::Atom::definitionProxy ) {
+ if ( makingDylib )
+ throwf("-init function (%s) found in linked dylib, must be in dylib being linked", symbolName);
+ else
+ throwf("entry point (%s) found in linked dylib, must be in executable being linked", symbolName);
+ }
return _internal.indirectBindingTable[slot];
}
return NULL;
if ( needsStubHelper && _options.makeCompressedDyldInfo() ) {
// "dyld_stub_binder" comes from libSystem.dylib so will need to manually resolve
if ( !_symbolTable.hasName("dyld_stub_binder") ) {
- _inputFiles.searchLibraries("dyld_stub_binder", true, false, *this);
+ _inputFiles.searchLibraries("dyld_stub_binder", true, false, false, *this);
}
if ( _symbolTable.hasName("dyld_stub_binder") ) {
SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName("dyld_stub_binder");
_internal.compressedFastBinderProxy = new UndefinedProxyAtom("dyld_stub_binder");
this->doAtom(*_internal.compressedFastBinderProxy);
}
- else {
- warning("symbol dyld_stub_binder not found, normally in libSystem.dylib");
- }
}
}
}
const char *targetName = *uit;
// these symbols may or may not already be in linker's symbol table
if ( ! _symbolTable.hasName(targetName) ) {
- _inputFiles.searchLibraries(targetName, true, true, *this);
+ _inputFiles.searchLibraries(targetName, true, true, false, *this);
}
}
_addToFinalSection = false;
case ld::Atom::definitionRegular:
if ( existingAtom->combine() == ld::Atom::combineByName ) {
if ( newAtom.combine() == ld::Atom::combineByName ) {
- // both weak, prefer non-auto-hide one
- if ( newAtom.autoHide() != existingAtom->autoHide() ) {
- // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden
- useNew = existingAtom->autoHide();
- // don't check for visibility mismatch
- }
- else if ( newAtom.autoHide() && existingAtom->autoHide() ) {
- // both have auto-hide, so use one with greater alignment
- useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() );
+ // <rdar://problem/9183821> always choose mach-o over llvm bit code, otherwise LTO may eliminate the llvm atom
+ const bool existingIsLTO = (existingAtom->contentType() == ld::Atom::typeLTOtemporary);
+ const bool newIsLTO = (newAtom.contentType() == ld::Atom::typeLTOtemporary);
+ if ( existingIsLTO != newIsLTO ) {
+ useNew = existingIsLTO;
}
else {
- // neither auto-hide, check visibility
- if ( newAtom.scope() != existingAtom->scope() ) {
- // <rdar://problem/8304984> use more visible weak def symbol
- useNew = (newAtom.scope() == ld::Atom::scopeGlobal);
+ // both weak, prefer non-auto-hide one
+ if ( newAtom.autoHide() != existingAtom->autoHide() ) {
+ // <rdar://problem/6783167> support auto hidden weak symbols: .weak_def_can_be_hidden
+ useNew = existingAtom->autoHide();
+ // don't check for visibility mismatch
}
- else {
- // both have same visibility, use one with greater alignment
+ else if ( newAtom.autoHide() && existingAtom->autoHide() ) {
+ // both have auto-hide, so use one with greater alignment
useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() );
}
+ else {
+ // neither auto-hide, check visibility
+ if ( newAtom.scope() != existingAtom->scope() ) {
+ // <rdar://problem/8304984> use more visible weak def symbol
+ useNew = (newAtom.scope() == ld::Atom::scopeGlobal);
+ }
+ else {
+ // both have same visibility, use one with greater alignment
+ useNew = ( newAtom.alignment().trailingZeros() > existingAtom->alignment().trailingZeros() );
+ }
+ }
}
}
else {
newAtom.name(), existingAtom->size(), existingAtom->file()->path(),
newAtom.size(), newAtom.file()->path());
}
+ if ( newAtom.section().type() == ld::Section::typeCode ) {
+ warning("for symbol %s tentative (data) defintion from %s is "
+ "being replaced by code from %s", newAtom.name(), existingAtom->file()->path(),
+ newAtom.file()->path());
+ }
break;
case ld::Atom::definitionTentative:
// new and existing are both tentative definitions, use largest
size_t filelen, dirlen;
uint64_t dir;
char * result;
+ const char * dirpath;
/* I'm not sure if this is actually an error. */
if (n == 0
else if (dir > lnd->numdir)
return NULL;
- dirlen = strlen ((const char *) lnd->dirnames[dir - 1]);
+ dirpath = (const char *)lnd->dirnames[dir - 1];
+ dirlen = strlen (dirpath);
+ if ( dirpath[dirlen-1] == '/' )
+ --dirlen;
+ if ( (dirpath[dirlen-1] == '.') && (dirpath[dirlen-2] == '/') )
+ dirlen -= 2;
result = malloc (dirlen + filelen + 2);
- memcpy (result, lnd->dirnames[dir - 1], dirlen);
+ memcpy (result, dirpath, dirlen);
result[dirlen] = '/';
memcpy (result + dirlen + 1, lnd->filenames[n - 1], filelen);
result[dirlen + 1 + filelen] = '\0';
DW_LNE_define_file
};
+
+// dwarf unwind instructions
+enum {
+ DW_CFA_nop = 0x0,
+ DW_CFA_set_loc = 0x1,
+ DW_CFA_advance_loc1 = 0x2,
+ DW_CFA_advance_loc2 = 0x3,
+ DW_CFA_advance_loc4 = 0x4,
+ DW_CFA_offset_extended = 0x5,
+ DW_CFA_restore_extended = 0x6,
+ DW_CFA_undefined = 0x7,
+ DW_CFA_same_value = 0x8,
+ DW_CFA_register = 0x9,
+ DW_CFA_remember_state = 0xA,
+ DW_CFA_restore_state = 0xB,
+ DW_CFA_def_cfa = 0xC,
+ DW_CFA_def_cfa_register = 0xD,
+ DW_CFA_def_cfa_offset = 0xE,
+ DW_CFA_def_cfa_expression = 0xF,
+ DW_CFA_expression = 0x10,
+ DW_CFA_offset_extended_sf = 0x11,
+ DW_CFA_def_cfa_sf = 0x12,
+ DW_CFA_def_cfa_offset_sf = 0x13,
+ DW_CFA_val_offset = 0x14,
+ DW_CFA_val_offset_sf = 0x15,
+ DW_CFA_val_expression = 0x16,
+ DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta
+ DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register
+ DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register
+
+ // GNU extensions
+ DW_CFA_GNU_window_save = 0x2D,
+ DW_CFA_GNU_args_size = 0x2E,
+ DW_CFA_GNU_negative_offset_extended = 0x2F
+};
+
+
+// FSF exception handling Pointer-Encoding constants
+// Used in CFI augmentation by gcc compiler
+enum {
+ DW_EH_PE_ptr = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_signed = 0x08,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0A,
+ DW_EH_PE_sdata4 = 0x0B,
+ DW_EH_PE_sdata8 = 0x0C,
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+ DW_EH_PE_indirect = 0x80,
+ DW_EH_PE_omit = 0xFF
+};
+
+
+// DWARF expressions
+enum {
+ DW_OP_addr = 0x03, // constant address (size target specific)
+ DW_OP_deref = 0x06,
+ DW_OP_const1u = 0x08, // 1-byte constant
+ DW_OP_const1s = 0x09, // 1-byte constant
+ DW_OP_const2u = 0x0A, // 2-byte constant
+ DW_OP_const2s = 0x0B, // 2-byte constant
+ DW_OP_const4u = 0x0C, // 4-byte constant
+ DW_OP_const4s = 0x0D, // 4-byte constant
+ DW_OP_const8u = 0x0E, // 8-byte constant
+ DW_OP_const8s = 0x0F, // 8-byte constant
+ DW_OP_constu = 0x10, // ULEB128 constant
+ DW_OP_consts = 0x11, // SLEB128 constant
+ DW_OP_dup = 0x12,
+ DW_OP_drop = 0x13,
+ DW_OP_over = 0x14,
+ DW_OP_pick = 0x15, // 1-byte stack index
+ DW_OP_swap = 0x16,
+ DW_OP_rot = 0x17,
+ DW_OP_xderef = 0x18,
+ DW_OP_abs = 0x19,
+ DW_OP_and = 0x1A,
+ DW_OP_div = 0x1B,
+ DW_OP_minus = 0x1C,
+ DW_OP_mod = 0x1D,
+ DW_OP_mul = 0x1E,
+ DW_OP_neg = 0x1F,
+ DW_OP_not = 0x20,
+ DW_OP_or = 0x21,
+ DW_OP_plus = 0x22,
+ DW_OP_plus_uconst = 0x23, // ULEB128 addend
+ DW_OP_shl = 0x24,
+ DW_OP_shr = 0x25,
+ DW_OP_shra = 0x26,
+ DW_OP_xor = 0x27,
+ DW_OP_skip = 0x2F, // signed 2-byte constant
+ DW_OP_bra = 0x28, // signed 2-byte constant
+ DW_OP_eq = 0x29,
+ DW_OP_ge = 0x2A,
+ DW_OP_gt = 0x2B,
+ DW_OP_le = 0x2C,
+ DW_OP_lt = 0x2D,
+ DW_OP_ne = 0x2E,
+ DW_OP_lit0 = 0x30, // Literal 0
+ DW_OP_lit1 = 0x31, // Literal 1
+ DW_OP_lit2 = 0x32, // Literal 2
+ DW_OP_lit3 = 0x33, // Literal 3
+ DW_OP_lit4 = 0x34, // Literal 4
+ DW_OP_lit5 = 0x35, // Literal 5
+ DW_OP_lit6 = 0x36, // Literal 6
+ DW_OP_lit7 = 0x37, // Literal 7
+ DW_OP_lit8 = 0x38, // Literal 8
+ DW_OP_lit9 = 0x39, // Literal 9
+ DW_OP_lit10 = 0x3A, // Literal 10
+ DW_OP_lit11 = 0x3B, // Literal 11
+ DW_OP_lit12 = 0x3C, // Literal 12
+ DW_OP_lit13 = 0x3D, // Literal 13
+ DW_OP_lit14 = 0x3E, // Literal 14
+ DW_OP_lit15 = 0x3F, // Literal 15
+ DW_OP_lit16 = 0x40, // Literal 16
+ DW_OP_lit17 = 0x41, // Literal 17
+ DW_OP_lit18 = 0x42, // Literal 18
+ DW_OP_lit19 = 0x43, // Literal 19
+ DW_OP_lit20 = 0x44, // Literal 20
+ DW_OP_lit21 = 0x45, // Literal 21
+ DW_OP_lit22 = 0x46, // Literal 22
+ DW_OP_lit23 = 0x47, // Literal 23
+ DW_OP_lit24 = 0x48, // Literal 24
+ DW_OP_lit25 = 0x49, // Literal 25
+ DW_OP_lit26 = 0x4A, // Literal 26
+ DW_OP_lit27 = 0x4B, // Literal 27
+ DW_OP_lit28 = 0x4C, // Literal 28
+ DW_OP_lit29 = 0x4D, // Literal 29
+ DW_OP_lit30 = 0x4E, // Literal 30
+ DW_OP_lit31 = 0x4F, // Literal 31
+ DW_OP_reg0 = 0x50, // Contents of reg0
+ DW_OP_reg1 = 0x51, // Contents of reg1
+ DW_OP_reg2 = 0x52, // Contents of reg2
+ DW_OP_reg3 = 0x53, // Contents of reg3
+ DW_OP_reg4 = 0x54, // Contents of reg4
+ DW_OP_reg5 = 0x55, // Contents of reg5
+ DW_OP_reg6 = 0x56, // Contents of reg6
+ DW_OP_reg7 = 0x57, // Contents of reg7
+ DW_OP_reg8 = 0x58, // Contents of reg8
+ DW_OP_reg9 = 0x59, // Contents of reg9
+ DW_OP_reg10 = 0x5A, // Contents of reg10
+ DW_OP_reg11 = 0x5B, // Contents of reg11
+ DW_OP_reg12 = 0x5C, // Contents of reg12
+ DW_OP_reg13 = 0x5D, // Contents of reg13
+ DW_OP_reg14 = 0x5E, // Contents of reg14
+ DW_OP_reg15 = 0x5F, // Contents of reg15
+ DW_OP_reg16 = 0x60, // Contents of reg16
+ DW_OP_reg17 = 0x61, // Contents of reg17
+ DW_OP_reg18 = 0x62, // Contents of reg18
+ DW_OP_reg19 = 0x63, // Contents of reg19
+ DW_OP_reg20 = 0x64, // Contents of reg20
+ DW_OP_reg21 = 0x65, // Contents of reg21
+ DW_OP_reg22 = 0x66, // Contents of reg22
+ DW_OP_reg23 = 0x67, // Contents of reg23
+ DW_OP_reg24 = 0x68, // Contents of reg24
+ DW_OP_reg25 = 0x69, // Contents of reg25
+ DW_OP_reg26 = 0x6A, // Contents of reg26
+ DW_OP_reg27 = 0x6B, // Contents of reg27
+ DW_OP_reg28 = 0x6C, // Contents of reg28
+ DW_OP_reg29 = 0x6D, // Contents of reg29
+ DW_OP_reg30 = 0x6E, // Contents of reg30
+ DW_OP_reg31 = 0x6F, // Contents of reg31
+ DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset
+ DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset
+ DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset
+ DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset
+ DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset
+ DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset
+ DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset
+ DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset
+ DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset
+ DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset
+ DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset
+ DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset
+ DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset
+ DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset
+ DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset
+ DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset
+ DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset
+ DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset
+ DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset
+ DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset
+ DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset
+ DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset
+ DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset
+ DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset
+ DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset
+ DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset
+ DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset
+ DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset
+ DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset
+ DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset
+ DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset
+ DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset
+ DW_OP_regx = 0x90, // ULEB128 register
+ DW_OP_fbreg = 0x91, // SLEB128 offset
+ DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset
+ DW_OP_piece = 0x93, // ULEB128 size of piece addressed
+ DW_OP_deref_size = 0x94, // 1-byte size of data retrieved
+ DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved
+ DW_OP_nop = 0x96,
+ DW_OP_push_object_addres = 0x97,
+ DW_OP_call2 = 0x98, // 2-byte offset of DIE
+ DW_OP_call4 = 0x99, // 4-byte offset of DIE
+ DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE
+ DW_OP_lo_user = 0xE0,
+ DW_OP_APPLE_uninit = 0xF0,
+ DW_OP_hi_user = 0xFF
+};
+
+
+
#endif
public:
FinalSection(const ld::Section& sect, uint32_t sectionsSeen, bool objFile);
static int sectionComparer(const void* l, const void* r);
- static const ld::Section& outputSection(const ld::Section& sect);
+ static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill);
static const ld::Section& objectOutputSection(const ld::Section& sect, bool makeTentativeDefsReal);
private:
friend class InternalState;
static ld::Section _s_TEXT_const;
static ld::Section _s_DATA_nl_symbol_ptr;
static ld::Section _s_DATA_common;
+ static ld::Section _s_DATA_zerofill;
};
ld::Section InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld::Section::typeUnclassified);
ld::Section InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer);
ld::Section InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill);
+ld::Section InternalState::FinalSection::_s_DATA_zerofill("__DATA", "__zerofill", ld::Section::typeZeroFill);
std::vector<const char*> InternalState::FinalSection::_s_segmentsSeen;
// this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder);
}
-const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect)
+const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect, bool mergeZeroFill)
{
// merge sections in final linked image
switch ( sect.type() ) {
return _s_TEXT_const;
}
break;
+ case ld::Section::typeZeroFill:
+ if ( mergeZeroFill )
+ return _s_DATA_zerofill;
+ break;
case ld::Section::typeCode:
if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) {
if ( strcmp(sect.sectionName(), "__textcoal_nt") == 0 )
}
break;
case ld::Section::typeTentativeDefs:
- return _s_DATA_common;
+ if ( mergeZeroFill )
+ return _s_DATA_zerofill;
+ else
+ return _s_DATA_common;
break;
// FIX ME: more
default:
switch ( atom.section().type() ) {
case ld::Section::typeZeroFill:
case ld::Section::typeTentativeDefs:
- if ( (_options.outputKind() == Options::kDyld) && (atom.symbolTableInclusion() == ld::Atom::symbolTableIn)
+ if ( (atom.symbolTableInclusion() == ld::Atom::symbolTableIn)
&& (atom.size() <= 512) && (_options.orderedSymbolsCount() != 0) ) {
for(Options::OrderedSymbolsIterator it = _options.orderedSymbolsBegin(); it != _options.orderedSymbolsEnd(); ++it) {
if ( (it->objectFileName == NULL) && (strcmp(it->symbolName, atom.name()) == 0) ) {
case Options::kPreload:
{
// coalesce some sections
- const ld::Section& outSect = FinalSection::outputSection(inputSection);
+ const ld::Section& outSect = FinalSection::outputSection(inputSection, _options.mergeZeroFill());
pos = _sectionInToFinalMap.find(&outSect);
if ( pos != _sectionInToFinalMap.end() ) {
_sectionInToFinalMap[&inputSection] = pos->second;
//
enum MacVersionMin { macVersionUnset=0, mac10_4=0x000A0400, mac10_5=0x000A0500,
mac10_6=0x000A0600, mac10_7=0x000A0700 };
-enum IPhoneVersionMin { iPhoneVersionUnset=0, iPhone2_0=0x00020000, iPhone3_1=0x00030100,
- iPhone4_2=0x00040200, iPhone4_3=0x00040300 };
+enum IOSVersionMin { iOSVersionUnset=0, iOS_2_0=0x00020000, iOS_3_1=0x00030100,
+ iOS_4_2=0x00040200, iOS_4_3=0x00040300, iOS_5_0=0x00050000 };
namespace relocatable {
//
: ld::File(pth, modTime, ord), _dylibInstallPath(NULL),
_dylibTimeStamp(0), _dylibCurrentVersion(0), _dylibCompatibilityVersion(0),
_explicitlyLinked(false), _implicitlyLinked(false),
- _lazyLoadedDylib(false), _weakLinked(false), _reExported(false),
- _upward(false), _hasNonWeakImportedSymbols(false),
- _hasWeakImportedSymbols(false), _dead(false) { }
+ _lazyLoadedDylib(false), _forcedWeakLinked(false), _reExported(false),
+ _upward(false), _dead(false) { }
const char* installPath() const { return _dylibInstallPath; }
uint32_t timestamp() const { return _dylibTimeStamp; }
uint32_t currentVersion() const { return _dylibCurrentVersion; }
// attributes of how dylib will be used when linked
void setWillBeLazyLoadedDylb() { _lazyLoadedDylib = true; }
bool willBeLazyLoadedDylib() const { return _lazyLoadedDylib; }
- void setWillBeWeakLinked() { _weakLinked = true; }
- bool willBeWeakLinked() const { return _weakLinked ||
- (_hasWeakImportedSymbols && !_hasNonWeakImportedSymbols); }
+ void setForcedWeakLinked() { _forcedWeakLinked = true; }
+ bool forcedWeakLinked() const { return _forcedWeakLinked; }
+
void setWillBeReExported() { _reExported = true; }
bool willBeReExported() const { return _reExported; }
void setWillBeUpwardDylib() { _upward = true; }
bool willBeUpwardDylib() const { return _upward; }
- void setUsingNonWeakImportedSymbols(){ _hasNonWeakImportedSymbols = true; }
- void setUsingWeakImportedSymbols() { _hasWeakImportedSymbols = true; }
void setWillBeRemoved(bool value) { _dead = value; }
bool willRemoved() const { return _dead; }
virtual bool deadStrippable() const = 0;
virtual bool hasWeakDefinition(const char* name) const = 0;
virtual bool hasPublicInstallName() const = 0;
+ virtual bool allSymbolsAreWeakImported() const = 0;
protected:
const char* _dylibInstallPath;
uint32_t _dylibTimeStamp;
bool _explicitlyLinked;
bool _implicitlyLinked;
bool _lazyLoadedDylib;
- bool _weakLinked;
+ bool _forcedWeakLinked;
bool _reExported;
bool _upward;
- bool _hasNonWeakImportedSymbols;
- bool _hasWeakImportedSymbols;
bool _dead;
};
} // namespace dylib
+namespace archive {
+ //
+ // ld::archive::File
+ //
+ // Abstract base class for static libraries read by the linker processes.
+ //
+ class File : public ld::File
+ {
+ public:
+ File(const char* pth, time_t modTime, uint32_t ord)
+ : ld::File(pth, modTime, ord) { }
+ virtual ~File() {}
+ virtual bool justInTimeDataOnlyforEachAtom(const char* name, AtomHandler&) const = 0;
+ };
+} // namespace archive
+
//
// ld::Section
typeLazyDylibPointer, typeStubHelper, typeInitializerPointers, typeTerminatorPointers,
typeStubClose, typeLazyPointerClose, typeAbsoluteSymbols,
typeTLVDefs, typeTLVZeroFill, typeTLVInitialValues, typeTLVInitializerPointers, typeTLVPointers,
- typeFirstSection, typeLastSection };
+ typeFirstSection, typeLastSection, typeDebug };
Section(const char* sgName, const char* sctName,
enum SymbolTableInclusion { symbolTableNotIn, symbolTableNotInFinalLinkedImages, symbolTableIn,
symbolTableInAndNeverStrip, symbolTableInAsAbsolute,
symbolTableInWithRandomAutoStripLabel };
+ enum WeakImportState { weakImportUnset, weakImportTrue, weakImportFalse };
+
struct Alignment {
Alignment(int p2, int m=0) : powerOf2(p2), modulus(m) {}
uint8_t trailingZeros() const { return (modulus==0) ? powerOf2 : __builtin_ctz(modulus); }
_contentType(ct), _symbolTableInclusion(i),
_scope(s), _mode(modeSectionOffset),
_overridesADylibsWeakDef(false), _coalescedAway(false),
- _weakImport(false), _live(false), _machoSection(0)
+ _live(false), _machoSection(0), _weakImportState(weakImportUnset)
{
#ifndef NDEBUG
switch ( _combine ) {
Alignment alignment() const { return Alignment(_alignmentPowerOf2, _alignmentModulus); }
bool overridesDylibsWeakDef() const { return _overridesADylibsWeakDef; }
bool coalescedAway() const { return _coalescedAway; }
- bool weakImported() const { return _weakImport; }
+ bool weakImported() const { return _weakImportState == weakImportTrue; }
+ WeakImportState weakImportState() const { return _weakImportState; }
bool autoHide() const { return _autoHide; }
bool live() const { return _live; }
uint8_t machoSection() const { assert(_machoSection != 0); return _machoSection; }
void setCombine(Combine c) { _combine = c; }
void setOverridesDylibsWeakDef() { _overridesADylibsWeakDef = true; }
void setCoalescedAway() { _coalescedAway = true; }
- void setWeakImported() { _weakImport = true; assert(_definition == definitionProxy); }
+ void setWeakImportState(bool w) { assert(_definition == definitionProxy); _weakImportState = ( w ? weakImportTrue : weakImportFalse); }
void setAutoHide() { _autoHide = true; }
void setLive() { _live = true; }
void setLive(bool value) { _live = value; }
_mode = a._mode;
_overridesADylibsWeakDef = a._overridesADylibsWeakDef;
_coalescedAway = a._coalescedAway;
- _weakImport = a._weakImport;
+ _weakImportState = a._weakImportState;
}
const Section * _section;
AddressMode _mode: 2;
bool _overridesADylibsWeakDef : 1;
bool _coalescedAway : 1;
- bool _weakImport : 1;
bool _live : 1;
unsigned _machoSection : 8;
+ WeakImportState _weakImportState : 2;
};
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <vector>
#include <set>
+#include <map>
#include <algorithm>
#include <ext/hash_map>
};
template <typename A>
-class File : public ld::File
+class File : public ld::archive::File
{
public:
static bool validFile(const uint8_t* fileContent, uint64_t fileLength,
virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const;
virtual uint32_t subFileCount() const { return _archiveFilelength/sizeof(ar_hdr); }
+ // overrides of ld::archive::File
+ virtual bool justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHandler& handler) const;
+
private:
static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength,
const mach_o::relocatable::ParserOptions& opts);
const mach_o::relocatable::ParserOptions& opts);
static cpu_type_t architecture();
-
class Entry : ar_hdr
{
public:
typedef typename A::P P;
typedef typename A::P::E E;
+ struct MemberState { ld::relocatable::File* file; bool logged; bool loaded; };
+
+ typedef std::map<const class Entry*, MemberState> MemberToStateMap;
+
const struct ranlib* ranlibHashSearch(const char* name) const;
- ld::relocatable::File* makeObjectFileForMember(const Entry* member) const;
+ MemberState& makeObjectFileForMember(const Entry* member) const;
bool memberHasObjCCategories(const Entry* member) const;
void dumpTableOfContents();
void buildHashTable();
const struct ranlib* _tableOfContents;
uint32_t _tableOfContentCount;
const char* _tableOfContentStrings;
- mutable std::vector<ld::relocatable::File*> _instantiatedFiles;
- mutable std::set<const class Entry*> _instantiatedEntries;
+ mutable MemberToStateMap _instantiatedEntries;
NameToEntryMap _hashTable;
const bool _forceLoadAll;
const bool _forceLoadObjC;
const bool _forceLoadThis;
+ const bool _objc2ABI;
const bool _verboseLoad;
const bool _logAllFiles;
const mach_o::relocatable::ParserOptions _objOpts;
template <typename A>
File<A>::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, time_t modTime,
uint32_t ord, const ParserOptions& opts)
- : ld::File(strdup(pth), modTime, ord),
+ : ld::archive::File(strdup(pth), modTime, ord),
_archiveFileContent(fileContent), _archiveFilelength(fileLength),
_tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL),
_forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC),
- _forceLoadThis(opts.forceLoadThisArchive), _verboseLoad(opts.verboseLoad),
+ _forceLoadThis(opts.forceLoadThisArchive), _objc2ABI(opts.objcABI2), _verboseLoad(opts.verboseLoad),
_logAllFiles(opts.logAllFiles), _objOpts(opts.objOpts)
{
if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
template <>
bool File<x86>::memberHasObjCCategories(const Entry* member) const
{
- // i386 uses ObjC1 ABI which has .objc_category* global symbols
- return false;
+ if ( _objc2ABI ) {
+ // i386 for iOS simulator uses ObjC2 which has no global symbol for categories
+ return mach_o::relocatable::hasObjC2Categories(member->content());
+ }
+ else {
+ // i386 uses ObjC1 ABI which has .objc_category* global symbols
+ return false;
+ }
}
template <>
template <typename A>
-ld::relocatable::File* File<A>::makeObjectFileForMember(const Entry* member) const
+typename File<A>::MemberState& File<A>::makeObjectFileForMember(const Entry* member) const
{
+ // in case member was instantiated earlier but not needed yet
+ typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member);
+ if ( pos != _instantiatedEntries.end() )
+ return pos->second;
+
const char* memberName = member->name();
char memberPath[strlen(this->path()) + strlen(memberName)+4];
strcpy(memberPath, this->path());
ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(),
mPath, member->modificationTime(),
this->ordinal() + memberIndex, _objOpts);
- if ( result != NULL )
- return result;
+ if ( result != NULL ) {
+ MemberState state = {result, false, false};
+ _instantiatedEntries[member] = state;
+ return _instantiatedEntries[member];
+ }
// see if member is llvm bitcode file
result = lto::parse(member->content(), member->contentSize(),
mPath, member->modificationTime(), this->ordinal() + memberIndex,
_objOpts.architecture, _objOpts.subType, _logAllFiles);
- if ( result != NULL )
- return result;
+ if ( result != NULL ) {
+ MemberState state = {result, false, false};
+ _instantiatedEntries[member] = state;
+ return _instantiatedEntries[member];
+ }
throwf("archive member '%s' with length %d is not mach-o or llvm bitcode", memberName, member->contentSize());
}
const char* memberName = p->name();
if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
continue;
+ MemberState& state = this->makeObjectFileForMember(p);
if ( _verboseLoad ) {
if ( _forceLoadThis )
printf("-force_load forced load of %s(%s)\n", this->path(), memberName);
else
printf("-all_load forced load of %s(%s)\n", this->path(), memberName);
+ state.logged = true;
}
- ld::relocatable::File* file = this->makeObjectFileForMember(p);
- didSome |= file->forEachAtom(handler);
+ didSome |= state.file->forEachAtom(handler);
+ state.loaded = true;
}
}
else if ( _forceLoadObjC ) {
for(typename NameToEntryMap::const_iterator it = _hashTable.begin(); it != _hashTable.end(); ++it) {
if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
const Entry* member = (Entry*)&_archiveFileContent[E::get32(it->second->ran_off)];
- if ( _instantiatedEntries.count(member) == 0 ) {
- if ( _verboseLoad )
- printf("-ObjC forced load of %s(%s)\n", this->path(), member->name());
- // only return these atoms once
- _instantiatedEntries.insert(member);
- ld::relocatable::File* file = this->makeObjectFileForMember(member);
- didSome |= file->forEachAtom(handler);
- _instantiatedFiles.push_back(file);
+ MemberState& state = this->makeObjectFileForMember(member);
+ if ( _verboseLoad && !state.logged ) {
+ printf("-ObjC forced load of %s(%s)\n", this->path(), member->name());
+ state.logged = true;
+ }
+ if ( ! state.loaded ) {
+ didSome |= state.file->forEachAtom(handler);
+ state.loaded = true;
}
}
}
- // ObjC2 has no symbols in .o files with categories, but not classes, look deeper for those
+ // ObjC2 has no symbols in .o files with categories but not classes, look deeper for those
const Entry* const start = (Entry*)&_archiveFileContent[8];
const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength];
for (const Entry* member=start; member < end; member = member->next()) {
if ( _instantiatedEntries.count(member) == 0 ) {
//fprintf(stderr, "checking member %s\n", member->name());
if ( this->memberHasObjCCategories(member) ) {
- if ( _verboseLoad )
+ MemberState& state = this->makeObjectFileForMember(member);
+ if ( _verboseLoad && !state.logged ) {
printf("-ObjC forced load of %s(%s)\n", this->path(), member->name());
- // only return these atoms once
- _instantiatedEntries.insert(member);
- ld::relocatable::File* file = this->makeObjectFileForMember(member);
- didSome |= file->forEachAtom(handler);
- _instantiatedFiles.push_back(file);
+ state.logged = true;
+ }
+ if ( ! state.loaded ) {
+ didSome |= state.file->forEachAtom(handler);
+ state.loaded = true;
+ }
}
}
}
const struct ranlib* result = ranlibHashSearch(name);
if ( result != NULL ) {
const Entry* member = (Entry*)&_archiveFileContent[E::get32(result->ran_off)];
+ MemberState& state = this->makeObjectFileForMember(member);
// only call handler for each member once
- if ( _instantiatedEntries.count(member) == 0 ) {
- _instantiatedEntries.insert(member);
- if ( _verboseLoad )
+ if ( ! state.loaded && !state.logged ) {
+ if ( _verboseLoad ) {
printf("%s forced load of %s(%s)\n", name, this->path(), member->name());
- ld::relocatable::File* file = this->makeObjectFileForMember(member);
- _instantiatedFiles.push_back(file);
- return file->forEachAtom(handler);
+ state.logged = true;
+ }
+ state.loaded = true;
+ return state.file->forEachAtom(handler);
+ }
+ }
+ //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path());
+ return false;
+}
+
+class CheckIsDataSymbolHandler : public ld::File::AtomHandler
+{
+public:
+ CheckIsDataSymbolHandler(const char* n) : _name(n), _isData(false) {}
+ virtual void doAtom(const class ld::Atom& atom) {
+ if ( strcmp(atom.name(), _name) == 0 ) {
+ if ( atom.section().type() != ld::Section::typeCode )
+ _isData = true;
+ }
+ }
+ virtual void doFile(const class ld::File&) {}
+ bool symbolIsDataDefinition() { return _isData; }
+
+private:
+ const char* _name;
+ bool _isData;
+
+};
+
+template <typename A>
+bool File<A>::justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHandler& handler) const
+{
+ // in force load case, all members already loaded
+ if ( _forceLoadAll || _forceLoadThis )
+ return false;
+
+ // do a hash search of table of contents looking for requested symbol
+ const struct ranlib* result = ranlibHashSearch(name);
+ if ( result != NULL ) {
+ const Entry* member = (Entry*)&_archiveFileContent[E::get32(result->ran_off)];
+ MemberState& state = this->makeObjectFileForMember(member);
+ // only call handler for each member once
+ if ( ! state.loaded ) {
+ CheckIsDataSymbolHandler checker(name);
+ state.file->forEachAtom(checker);
+ if ( checker.symbolIsDataDefinition() ) {
+ if ( _verboseLoad && !state.logged ) {
+ printf("%s forced load of %s(%s)\n", name, this->path(), member->name());
+ state.logged = true;
+ }
+ state.loaded = true;
+ return state.file->forEachAtom(handler);
+ }
}
}
//fprintf(stderr, "%s NOT found in archive %s\n", name, this->path());
//
// main function used by linker to instantiate archive files
//
-ld::File* parse(const uint8_t* fileContent, uint64_t fileLength,
+ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts)
{
switch ( opts.objOpts.architecture ) {
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
bool forceLoadThisArchive;
bool forceLoadAll;
bool forceLoadObjC;
+ bool objcABI2;
bool verboseLoad;
bool logAllFiles;
};
-extern ld::File* parse(const uint8_t* fileContent, uint64_t fileLength,
+extern ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t modTime, uint32_t ordinal, const ParserOptions& opts);
} // namespace archive
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// C++ interface to lower levels of libuwind
+//
+
+#ifndef __ADDRESSSPACE_HPP__
+#define __ADDRESSSPACE_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/loader.h>
+#include <mach-o/getsect.h>
+#include <mach-o/dyld_priv.h>
+#include <mach/i386/thread_status.h>
+#include <Availability.h>
+
+#include "FileAbstraction.hpp"
+#include "libunwind.h"
+#include "InternalMacros.h"
+#include "dwarf2.h"
+
+
+#if 0
+#if __i386__ || __x86_64__
+// In 10.6 and later i386 and x86_64 don't have a __dyld section
+// We need one to access private _dyld_func_lookup function.
+
+struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); };
+
+static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL };
+
+
+static int my_dyld_func_lookup(const char* dyld_func_name, void **address)
+{
+ return (*myDyldSection.lookup)(dyld_func_name, address);
+}
+#else
+ #define my_dyld_func_lookup _dyld_func_lookup
+#endif
+
+
+bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
+{
+ static void* (*p)(void*, dyld_unwind_sections*) = NULL;
+
+ if(p == NULL)
+ my_dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p);
+ return p(addr, info);
+}
+#endif // 0
+
+
+
+namespace libunwind {
+
+///
+/// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread
+/// in the same process. It compiles away and making local unwinds very fast.
+///
+class LocalAddressSpace
+{
+public:
+
+ #if __LP64__
+ typedef uint64_t pint_t;
+ typedef int64_t sint_t;
+ #else
+ typedef uint32_t pint_t;
+ typedef int32_t sint_t;
+ #endif
+ uint8_t get8(pint_t addr) { return *((uint8_t*)addr); }
+ uint16_t get16(pint_t addr) { return *((uint16_t*)addr); }
+ uint32_t get32(pint_t addr) { return *((uint32_t*)addr); }
+ uint64_t get64(pint_t addr) { return *((uint64_t*)addr); }
+ double getDouble(pint_t addr) { return *((double*)addr); }
+ v128 getVector(pint_t addr) { return *((v128*)addr); }
+ uintptr_t getP(pint_t addr);
+ static uint64_t getULEB128(pint_t& addr, pint_t end);
+ static int64_t getSLEB128(pint_t& addr, pint_t end);
+
+ pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding);
+ bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset);
+ bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart);
+
+};
+
+LocalAddressSpace sThisAddress;
+
+inline uintptr_t LocalAddressSpace::getP(pint_t addr)
+{
+#if __LP64__
+ return get64(addr);
+#else
+ return get32(addr);
+#endif
+}
+
+/* Read a ULEB128 into a 64-bit word. */
+inline uint64_t
+LocalAddressSpace::getULEB128(pint_t& addr, pint_t end)
+{
+ const uint8_t* p = (uint8_t*)addr;
+ const uint8_t* pend = (uint8_t*)end;
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ uint64_t b;
+
+ if ( p == pend )
+ ABORT("truncated uleb128 expression");
+
+ b = *p & 0x7f;
+
+ if (bit >= 64 || b << bit >> bit != b) {
+ ABORT("malformed uleb128 expression");
+ }
+ else {
+ result |= b << bit;
+ bit += 7;
+ }
+ } while ( *p++ >= 0x80 );
+ addr = (pint_t)p;
+ return result;
+}
+
+/* Read a SLEB128 into a 64-bit word. */
+inline int64_t
+LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end)
+{
+ const uint8_t* p = (uint8_t*)addr;
+ int64_t result = 0;
+ int bit = 0;
+ uint8_t byte;
+ do {
+ byte = *p++;
+ result |= ((byte & 0x7f) << bit);
+ bit += 7;
+ } while (byte & 0x80);
+ // sign extend negative numbers
+ if ( (byte & 0x40) != 0 )
+ result |= (-1LL) << bit;
+ addr = (pint_t)p;
+ return result;
+}
+
+LocalAddressSpace::pint_t
+LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding)
+{
+ pint_t startAddr = addr;
+ const uint8_t* p = (uint8_t*)addr;
+ pint_t result;
+
+ // first get value
+ switch (encoding & 0x0F) {
+ case DW_EH_PE_ptr:
+ result = getP(addr);
+ p += sizeof(pint_t);
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_uleb128:
+ result = getULEB128(addr, end);
+ break;
+ case DW_EH_PE_udata2:
+ result = get16(addr);
+ p += 2;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_udata4:
+ result = get32(addr);
+ p += 4;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_udata8:
+ result = get64(addr);
+ p += 8;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sleb128:
+ result = getSLEB128(addr, end);
+ break;
+ case DW_EH_PE_sdata2:
+ result = (int16_t)get16(addr);
+ p += 2;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sdata4:
+ result = (int32_t)get32(addr);
+ p += 4;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sdata8:
+ result = get64(addr);
+ p += 8;
+ addr = (pint_t)p;
+ break;
+ default:
+ ABORT("unknown pointer encoding");
+ }
+
+ // then add relative offset
+ switch ( encoding & 0x70 ) {
+ case DW_EH_PE_absptr:
+ // do nothing
+ break;
+ case DW_EH_PE_pcrel:
+ result += startAddr;
+ break;
+ case DW_EH_PE_textrel:
+ ABORT("DW_EH_PE_textrel pointer encoding not supported");
+ break;
+ case DW_EH_PE_datarel:
+ ABORT("DW_EH_PE_datarel pointer encoding not supported");
+ break;
+ case DW_EH_PE_funcrel:
+ ABORT("DW_EH_PE_funcrel pointer encoding not supported");
+ break;
+ case DW_EH_PE_aligned:
+ ABORT("DW_EH_PE_aligned pointer encoding not supported");
+ break;
+ default:
+ ABORT("unknown pointer encoding");
+ break;
+ }
+
+ if ( encoding & DW_EH_PE_indirect )
+ result = getP(result);
+
+ return result;
+}
+
+
+inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart)
+{
+ dyld_unwind_sections info;
+ if ( _dyld_find_unwind_sections((void*)addr, &info) ) {
+ mh = (pint_t)info.mh;
+ dwarfStart = (pint_t)info.dwarf_section;
+ dwarfLen = (pint_t)info.dwarf_section_length;
+ compactStart = (pint_t)info.compact_unwind_section;
+ return true;
+ }
+ return false;
+}
+
+
+inline bool LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset)
+{
+ dl_info dyldInfo;
+ if ( dladdr((void*)addr, &dyldInfo) ) {
+ if ( dyldInfo.dli_sname != NULL ) {
+ strlcpy(buf, dyldInfo.dli_sname, bufLen);
+ *offset = (addr - (pint_t)dyldInfo.dli_saddr);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+#if UNW_REMOTE
+
+///
+/// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread
+/// in the another process. The other process can be a different endianness and a different
+/// pointer size and is handled by the P template parameter.
+///
+template <typename P>
+class OtherAddressSpace
+{
+public:
+ OtherAddressSpace(task_t task) : fTask(task) {}
+
+ typedef typename P::uint_t pint_t;
+
+ uint8_t get8(pint_t addr);
+ uint16_t get16(pint_t addr);
+ uint32_t get32(pint_t addr);
+ uint64_t get64(pint_t addr);
+ pint_t getP(pint_t addr);
+ uint64_t getULEB128(pint_t& addr, pint_t end);
+ int64_t getSLEB128(pint_t& addr, pint_t end);
+ pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding);
+ bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset);
+ bool findUnwindSections(pint_t addr, unwind_sections& info);
+private:
+ void* localCopy(pint_t addr);
+
+
+ task_t fTask;
+};
+
+
+template <typename P>
+uint8_t OtherAddressSpace<P>::get8(pint_t addr)
+{
+ return *((uint8_t*)localCopy(addr));
+}
+
+template <typename P>
+uint16_t OtherAddressSpace<P>::get16(pint_t addr)
+{
+ return P::E::get16(*(uint16_t*)localCopy(addr));
+}
+
+template <typename P>
+uint32_t OtherAddressSpace<P>::get32(pint_t addr)
+{
+ return P::E::get32(*(uint32_t*)localCopy(addr));
+}
+
+template <typename P>
+uint64_t OtherAddressSpace<P>::get64(pint_t addr)
+{
+ return P::E::get64(*(uint64_t*)localCopy(addr));
+}
+
+template <typename P>
+typename P::uint_t OtherAddressSpace<P>::getP(pint_t addr)
+{
+ return P::getP(*(uint64_t*)localCopy(addr));
+}
+
+template <typename P>
+uint64_t OtherAddressSpace<P>::getULEB128(pint_t& addr, pint_t end)
+{
+ uintptr_t size = (end - addr);
+ LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr);
+ LocalAddressSpace::pint_t sladdr = laddr;
+ uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr+size);
+ addr += (laddr-sladdr);
+ return result;
+}
+
+template <typename P>
+int64_t OtherAddressSpace<P>::getSLEB128(pint_t& addr, pint_t end)
+{
+ uintptr_t size = (end - addr);
+ LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr);
+ LocalAddressSpace::pint_t sladdr = laddr;
+ uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr+size);
+ addr += (laddr-sladdr);
+ return result;
+}
+
+template <typename P>
+void* OtherAddressSpace<P>::localCopy(pint_t addr)
+{
+ // FIX ME
+}
+
+template <typename P>
+bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset)
+{
+ // FIX ME
+}
+
+
+
+///
+/// unw_addr_space is the base class that abstract unw_addr_space_t type in libunwind.h points to.
+///
+struct unw_addr_space
+{
+ cpu_type_t cpuType;
+ task_t taskPort;
+};
+
+
+///
+/// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points to when examining
+/// a 32-bit intel process.
+///
+struct unw_addr_space_i386 : public unw_addr_space
+{
+ unw_addr_space_i386(task_t task) : oas(task) {}
+ OtherAddressSpace<Pointer32<LittleEndian> > oas;
+};
+
+
+///
+/// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t points to when examining
+/// a 64-bit intel process.
+///
+struct unw_addr_space_x86_64 : public unw_addr_space
+{
+ unw_addr_space_x86_64(task_t task) : oas(task) {}
+ OtherAddressSpace<Pointer64<LittleEndian> > oas;
+};
+
+
+///
+/// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points to when examining
+/// a 32-bit PowerPC process.
+///
+struct unw_addr_space_ppc : public unw_addr_space
+{
+ unw_addr_space_ppc(task_t task) : oas(task) {}
+ OtherAddressSpace<Pointer32<BigEndian> > oas;
+};
+
+
+#endif // UNW_REMOTE
+
+
+} // namespace libunwind
+
+
+
+#endif // __ADDRESSSPACE_HPP__
+
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// processor specific parsing of dwarf unwind instructions
+//
+
+#ifndef __DWARF_INSTRUCTIONS_HPP__
+#define __DWARF_INSTRUCTIONS_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <libunwind.h>
+#include <mach-o/compact_unwind_encoding.h>
+
+#include "dwarf2.h"
+#include "AddressSpace.hpp"
+#include "Registers.hpp"
+#include "DwarfParser.hpp"
+#include "InternalMacros.h"
+//#include "CompactUnwinder.hpp"
+
+#define EXTRACT_BITS(value, mask) \
+ ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
+
+#define CFI_INVALID_ADDRESS ((pint_t)(-1))
+
+namespace libunwind {
+
+///
+/// Used by linker when parsing __eh_frame section
+///
+template <typename A>
+struct CFI_Reference {
+ typedef typename A::pint_t pint_t;
+ uint8_t encodingOfTargetAddress;
+ uint32_t offsetInCFI;
+ pint_t targetAddress;
+};
+template <typename A>
+struct CFI_Atom_Info {
+ typedef typename A::pint_t pint_t;
+ pint_t address;
+ uint32_t size;
+ bool isCIE;
+ union {
+ struct {
+ CFI_Reference<A> function;
+ CFI_Reference<A> cie;
+ CFI_Reference<A> lsda;
+ uint32_t compactUnwindInfo;
+ } fdeInfo;
+ struct {
+ CFI_Reference<A> personality;
+ } cieInfo;
+ } u;
+};
+
+typedef void (*WarnFunc)(void* ref, uint64_t funcAddr, const char* msg);
+
+///
+/// DwarfInstructions maps abtract dwarf unwind instructions to a particular architecture
+///
+template <typename A, typename R>
+class DwarfInstructions
+{
+public:
+ typedef typename A::pint_t pint_t;
+ typedef typename A::sint_t sint_t;
+
+ static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ CFI_Atom_Info<A>* infos, uint32_t infosCount, void* ref, WarnFunc warn);
+
+
+ static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart,
+ pint_t* lsda, pint_t* personality,
+ char warningBuffer[1024]);
+
+ static int stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers);
+
+private:
+
+ enum {
+ DW_X86_64_RET_ADDR = 16
+ };
+
+ enum {
+ DW_X86_RET_ADDR = 8
+ };
+
+ static pint_t evaluateExpression(pint_t expression, A& addressSpace, const R& registers, pint_t initialStackValue);
+ static pint_t getSavedRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg);
+ static double getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg);
+ static v128 getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg);
+
+ // x86 specific variants
+ static int lastRestoreReg(const Registers_x86&);
+ static bool isReturnAddressRegister(int regNum, const Registers_x86&);
+ static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_x86&);
+
+ static uint32_t getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure);
+ static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86&);
+ static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86&, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024]);
+
+ // x86_64 specific variants
+ static int lastRestoreReg(const Registers_x86_64&);
+ static bool isReturnAddressRegister(int regNum, const Registers_x86_64&);
+ static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_x86_64&);
+
+ static uint32_t getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure);
+ static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86_64&);
+ static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86_64&, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024]);
+
+ // ppc specific variants
+ static int lastRestoreReg(const Registers_ppc&);
+ static bool isReturnAddressRegister(int regNum, const Registers_ppc&);
+ static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_ppc&);
+ static compact_unwind_encoding_t encodeToUseDwarf(const Registers_ppc&);
+ static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_ppc&, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024]);
+};
+
+
+
+
+template <typename A, typename R>
+const char* DwarfInstructions<A,R>::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ CFI_Atom_Info<A>* infos, uint32_t infosCount, void* ref, WarnFunc warn)
+{
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ CFI_Atom_Info<A>* entry = infos;
+ CFI_Atom_Info<A>* end = &infos[infosCount];
+ const pint_t ehSectionEnd = ehSectionStart + sectionLength;
+ for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
+ pint_t currentCFI = p;
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return NULL; // end marker
+ if ( entry >= end )
+ return "too little space allocated for parseCFIs";
+ pint_t nextCFI = p + cfiLength;
+ uint32_t id = addressSpace.get32(p);
+ if ( id == 0 ) {
+ // is CIE
+ const char* err = CFI_Parser<A>::parseCIE(addressSpace, currentCFI, &cieInfo);
+ if ( err != NULL )
+ return err;
+ entry->address = currentCFI;
+ entry->size = nextCFI - currentCFI;
+ entry->isCIE = true;
+ entry->u.cieInfo.personality.targetAddress = cieInfo.personality;
+ entry->u.cieInfo.personality.offsetInCFI = cieInfo.personalityOffsetInCIE;
+ entry->u.cieInfo.personality.encodingOfTargetAddress = cieInfo.personalityEncoding;
+ ++entry;
+ }
+ else {
+ // is FDE
+ entry->address = currentCFI;
+ entry->size = nextCFI - currentCFI;
+ entry->isCIE = false;
+ entry->u.fdeInfo.function.targetAddress = CFI_INVALID_ADDRESS;
+ entry->u.fdeInfo.cie.targetAddress = CFI_INVALID_ADDRESS;
+ entry->u.fdeInfo.lsda.targetAddress = CFI_INVALID_ADDRESS;
+ uint32_t ciePointer = addressSpace.get32(p);
+ pint_t cieStart = p-ciePointer;
+ // validate pointer to CIE is within section
+ if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) )
+ return "FDE points to CIE outside __eh_frame section";
+ // optimize usual case where cie is same for all FDEs
+ if ( cieStart != cieInfo.cieStart ) {
+ const char* err = CFI_Parser<A>::parseCIE(addressSpace, cieStart, &cieInfo);
+ if ( err != NULL )
+ return err;
+ }
+ entry->u.fdeInfo.cie.targetAddress = cieStart;
+ entry->u.fdeInfo.cie.offsetInCFI = p-currentCFI;
+ entry->u.fdeInfo.cie.encodingOfTargetAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel;
+ p += 4;
+ // parse pc begin and range
+ pint_t offsetOfFunctionAddress = p-currentCFI;
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F);
+ //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // test if pc is within the function this FDE covers
+ entry->u.fdeInfo.function.targetAddress = pcStart;
+ entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress;
+ entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding;
+ // check for augmentation length
+ if ( cieInfo.fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( cieInfo.lsdaEncoding != 0 ) {
+ // peek at value (without indirection). Zero means no lsda
+ pint_t lsdaStart = p;
+ if ( addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding & 0x0F) != 0 ) {
+ // reset pointer and re-parse lsda address
+ p = lsdaStart;
+ pint_t offsetOfLSDAAddress = p-currentCFI;
+ entry->u.fdeInfo.lsda.targetAddress = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding);
+ entry->u.fdeInfo.lsda.offsetInCFI = offsetOfLSDAAddress;
+ entry->u.fdeInfo.lsda.encodingOfTargetAddress = cieInfo.lsdaEncoding;
+ }
+ }
+ p = endOfAug;
+ }
+ // compute compact unwind encoding
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ fdeInfo.fdeStart = currentCFI;
+ fdeInfo.fdeLength = nextCFI - currentCFI;
+ fdeInfo.fdeInstructions = p;
+ fdeInfo.pcStart = pcStart;
+ fdeInfo.pcEnd = pcStart + pcRange;
+ fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress;
+ typename CFI_Parser<A>::PrologInfo prolog;
+ R dummy; // for proper selection of architecture specific functions
+ if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) {
+ char warningBuffer[1024];
+ entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer);
+ if ( fdeInfo.lsda != CFI_INVALID_ADDRESS )
+ entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA;
+ if ( warningBuffer[0] != '\0' )
+ warn(ref, fdeInfo.pcStart, warningBuffer);
+ }
+ else {
+ warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed");
+ entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy);
+ }
+ ++entry;
+ }
+ p = nextCFI;
+ }
+ if ( entry != end )
+ return "wrong entry count for parseCFIs";
+ return NULL; // success
+}
+
+
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart,
+ pint_t* lsda, pint_t* personality,
+ char warningBuffer[1024])
+{
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ R dummy; // for proper selection of architecture specific functions
+ if ( CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) {
+ typename CFI_Parser<A>::PrologInfo prolog;
+ if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) {
+ *lsda = fdeInfo.lsda;
+ *personality = cieInfo.personality;
+ compact_unwind_encoding_t encoding;
+ encoding = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer);
+ if ( fdeInfo.lsda != 0 )
+ encoding |= UNWIND_HAS_LSDA;
+ return encoding;
+ }
+ else {
+ strcpy(warningBuffer, "dwarf unwind instructions could not be parsed");
+ return encodeToUseDwarf(dummy);
+ }
+ }
+ else {
+ strcpy(warningBuffer, "dwarf FDE could not be parsed");
+ return encodeToUseDwarf(dummy);
+ }
+}
+
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getSavedRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg)
+{
+ switch ( savedReg.location ) {
+ case CFI_Parser<A>::kRegisterInCFA:
+ return addressSpace.getP(cfa + savedReg.value);
+
+ case CFI_Parser<A>::kRegisterAtExpression:
+ return addressSpace.getP(evaluateExpression(savedReg.value, addressSpace, registers, cfa));
+
+ case CFI_Parser<A>::kRegisterIsExpression:
+ return evaluateExpression(savedReg.value, addressSpace, registers, cfa);
+
+ case CFI_Parser<A>::kRegisterInRegister:
+ return registers.getRegister(savedReg.value);
+
+ case CFI_Parser<A>::kRegisterUnused:
+ case CFI_Parser<A>::kRegisterOffsetFromCFA:
+ // FIX ME
+ break;
+ }
+ ABORT("unsupported restore location for register");
+}
+
+template <typename A, typename R>
+double DwarfInstructions<A,R>::getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg)
+{
+ switch ( savedReg.location ) {
+ case CFI_Parser<A>::kRegisterInCFA:
+ return addressSpace.getDouble(cfa + savedReg.value);
+
+ case CFI_Parser<A>::kRegisterAtExpression:
+ return addressSpace.getDouble(evaluateExpression(savedReg.value, addressSpace, registers, cfa));
+
+ case CFI_Parser<A>::kRegisterIsExpression:
+ case CFI_Parser<A>::kRegisterUnused:
+ case CFI_Parser<A>::kRegisterOffsetFromCFA:
+ case CFI_Parser<A>::kRegisterInRegister:
+ // FIX ME
+ break;
+ }
+ ABORT("unsupported restore location for float register");
+}
+
+template <typename A, typename R>
+v128 DwarfInstructions<A,R>::getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg)
+{
+ switch ( savedReg.location ) {
+ case CFI_Parser<A>::kRegisterInCFA:
+ return addressSpace.getVector(cfa + savedReg.value);
+
+ case CFI_Parser<A>::kRegisterAtExpression:
+ return addressSpace.getVector(evaluateExpression(savedReg.value, addressSpace, registers, cfa));
+
+ case CFI_Parser<A>::kRegisterIsExpression:
+ case CFI_Parser<A>::kRegisterUnused:
+ case CFI_Parser<A>::kRegisterOffsetFromCFA:
+ case CFI_Parser<A>::kRegisterInRegister:
+ // FIX ME
+ break;
+ }
+ ABORT("unsupported restore location for vector register");
+}
+
+
+template <typename A, typename R>
+int DwarfInstructions<A,R>::stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers)
+{
+ //fprintf(stderr, "stepWithDwarf(pc=0x%0llX, fdeStart=0x%0llX)\n", (uint64_t)pc, (uint64_t)fdeStart);
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ if ( CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) {
+ typename CFI_Parser<A>::PrologInfo prolog;
+ if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, &prolog) ) {
+ R newRegisters = registers;
+
+ // get pointer to cfa (architecture specific)
+ pint_t cfa = getCFA(addressSpace, prolog, registers);
+
+ // restore registers that dwarf says were saved
+ pint_t returnAddress = 0;
+ for (int i=0; i <= lastRestoreReg(newRegisters); ++i) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) {
+ if ( registers.validFloatRegister(i) )
+ newRegisters.setFloatRegister(i, getSavedFloatRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]));
+ else if ( registers.validVectorRegister(i) )
+ newRegisters.setVectorRegister(i, getSavedVectorRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]));
+ else if ( isReturnAddressRegister(i, registers) )
+ returnAddress = getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]);
+ else if ( registers.validRegister(i) )
+ newRegisters.setRegister(i, getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]));
+ else
+ return UNW_EBADREG;
+ }
+ }
+
+ // by definition the CFA is the stack pointer at the call site, so restoring SP means setting it to CFA
+ newRegisters.setSP(cfa);
+
+ // return address is address after call site instruction, so setting IP to that does a return
+ newRegisters.setIP(returnAddress);
+
+ // do the actual step by replacing the register set with the new ones
+ registers = newRegisters;
+
+ return UNW_STEP_SUCCESS;
+ }
+ }
+ return UNW_EBADFRAME;
+}
+
+
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::evaluateExpression(pint_t expression, A& addressSpace,
+ const R& registers, pint_t initialStackValue)
+{
+ const bool log = false;
+ pint_t p = expression;
+ pint_t expressionEnd = expression+20; // just need something until length is read
+ uint64_t length = addressSpace.getULEB128(p, expressionEnd);
+ expressionEnd = p + length;
+ if (log) fprintf(stderr, "evaluateExpression(): length=%llu\n", length);
+ pint_t stack[100];
+ pint_t* sp = stack;
+ *(++sp) = initialStackValue;
+
+ while ( p < expressionEnd ) {
+ if (log) {
+ for(pint_t* t = sp; t > stack; --t) {
+ fprintf(stderr, "sp[] = 0x%llX\n", (uint64_t)(*t));
+ }
+ }
+ uint8_t opcode = addressSpace.get8(p++);
+ sint_t svalue;
+ pint_t value;
+ uint32_t reg;
+ switch (opcode) {
+ case DW_OP_addr:
+ // push immediate address sized value
+ value = addressSpace.getP(p);
+ p += sizeof(pint_t);
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_deref:
+ // pop stack, dereference, push result
+ value = *sp--;
+ *(++sp) = addressSpace.getP(value);
+ if (log) fprintf(stderr, "dereference 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const1u:
+ // push immediate 1 byte value
+ value = addressSpace.get8(p);
+ p += 1;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const1s:
+ // push immediate 1 byte signed value
+ svalue = (int8_t)addressSpace.get8(p);
+ p += 1;
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_const2u:
+ // push immediate 2 byte value
+ value = addressSpace.get16(p);
+ p += 2;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const2s:
+ // push immediate 2 byte signed value
+ svalue = (int16_t)addressSpace.get16(p);
+ p += 2;
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_const4u:
+ // push immediate 4 byte value
+ value = addressSpace.get32(p);
+ p += 4;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const4s:
+ // push immediate 4 byte signed value
+ svalue = (int32_t)addressSpace.get32(p);
+ p += 4;
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_const8u:
+ // push immediate 8 byte value
+ value = addressSpace.get64(p);
+ p += 8;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const8s:
+ // push immediate 8 byte signed value
+ value = (int32_t)addressSpace.get64(p);
+ p += 8;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_constu:
+ // push immediate ULEB128 value
+ value = addressSpace.getULEB128(p, expressionEnd);
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_consts:
+ // push immediate SLEB128 value
+ svalue = addressSpace.getSLEB128(p, expressionEnd);
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_dup:
+ // push top of stack
+ value = *sp;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "duplicate top of stack\n");
+ break;
+
+ case DW_OP_drop:
+ // pop
+ --sp;
+ if (log) fprintf(stderr, "pop top of stack\n");
+ break;
+
+ case DW_OP_over:
+ // dup second
+ value = sp[-1];
+ *(++sp) = value;
+ if (log) fprintf(stderr, "duplicate second in stack\n");
+ break;
+
+ case DW_OP_pick:
+ // pick from
+ reg = addressSpace.get8(p);
+ p += 1;
+ value = sp[-reg];
+ *(++sp) = value;
+ if (log) fprintf(stderr, "duplicate %d in stack\n", reg);
+ break;
+
+ case DW_OP_swap:
+ // swap top two
+ value = sp[0];
+ sp[0] = sp[-1];
+ sp[-1] = value;
+ if (log) fprintf(stderr, "swap top of stack\n");
+ break;
+
+ case DW_OP_rot:
+ // rotate top three
+ value = sp[0];
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = value;
+ if (log) fprintf(stderr, "rotate top three of stack\n");
+ break;
+
+ case DW_OP_xderef:
+ // pop stack, dereference, push result
+ value = *sp--;
+ *sp = *((uint64_t*)value);
+ if (log) fprintf(stderr, "x-dereference 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_abs:
+ svalue = *sp;
+ if ( svalue < 0 )
+ *sp = -svalue;
+ if (log) fprintf(stderr, "abs\n");
+ break;
+
+ case DW_OP_and:
+ value = *sp--;
+ *sp &= value;
+ if (log) fprintf(stderr, "and\n");
+ break;
+
+ case DW_OP_div:
+ svalue = *sp--;
+ *sp = *sp / svalue;
+ if (log) fprintf(stderr, "div\n");
+ break;
+
+ case DW_OP_minus:
+ svalue = *sp--;
+ *sp = *sp - svalue;
+ if (log) fprintf(stderr, "minus\n");
+ break;
+
+ case DW_OP_mod:
+ svalue = *sp--;
+ *sp = *sp % svalue;
+ if (log) fprintf(stderr, "module\n");
+ break;
+
+ case DW_OP_mul:
+ svalue = *sp--;
+ *sp = *sp * svalue;
+ if (log) fprintf(stderr, "mul\n");
+ break;
+
+ case DW_OP_neg:
+ *sp = 0 - *sp;
+ if (log) fprintf(stderr, "neg\n");
+ break;
+
+ case DW_OP_not:
+ svalue = *sp;
+ *sp = ~svalue;
+ if (log) fprintf(stderr, "not\n");
+ break;
+
+ case DW_OP_or:
+ value = *sp--;
+ *sp |= value;
+ if (log) fprintf(stderr, "or\n");
+ break;
+
+ case DW_OP_plus:
+ value = *sp--;
+ *sp += value;
+ if (log) fprintf(stderr, "plus\n");
+ break;
+
+ case DW_OP_plus_uconst:
+ // pop stack, add uelb128 constant, push result
+ *sp += addressSpace.getULEB128(p, expressionEnd);
+ if (log) fprintf(stderr, "add constant\n");
+ break;
+
+ case DW_OP_shl:
+ value = *sp--;
+ *sp = *sp << value;
+ if (log) fprintf(stderr, "shift left\n");
+ break;
+
+ case DW_OP_shr:
+ value = *sp--;
+ *sp = *sp >> value;
+ if (log) fprintf(stderr, "shift left\n");
+ break;
+
+ case DW_OP_shra:
+ value = *sp--;
+ svalue = *sp;
+ *sp = svalue >> value;
+ if (log) fprintf(stderr, "shift left arithmetric\n");
+ break;
+
+ case DW_OP_xor:
+ value = *sp--;
+ *sp ^= value;
+ if (log) fprintf(stderr, "xor\n");
+ break;
+
+ case DW_OP_skip:
+ svalue = (int16_t)addressSpace.get16(p);
+ p += 2;
+ p += svalue;
+ if (log) fprintf(stderr, "skip %lld\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_bra:
+ svalue = (int16_t)addressSpace.get16(p);
+ p += 2;
+ if ( *sp-- )
+ p += svalue;
+ if (log) fprintf(stderr, "bra %lld\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_eq:
+ value = *sp--;
+ *sp = (*sp == value);
+ if (log) fprintf(stderr, "eq\n");
+ break;
+
+ case DW_OP_ge:
+ value = *sp--;
+ *sp = (*sp >= value);
+ if (log) fprintf(stderr, "ge\n");
+ break;
+
+ case DW_OP_gt:
+ value = *sp--;
+ *sp = (*sp > value);
+ if (log) fprintf(stderr, "gt\n");
+ break;
+
+ case DW_OP_le:
+ value = *sp--;
+ *sp = (*sp <= value);
+ if (log) fprintf(stderr, "le\n");
+ break;
+
+ case DW_OP_lt:
+ value = *sp--;
+ *sp = (*sp < value);
+ if (log) fprintf(stderr, "lt\n");
+ break;
+
+ case DW_OP_ne:
+ value = *sp--;
+ *sp = (*sp != value);
+ if (log) fprintf(stderr, "ne\n");
+ break;
+
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+ value = opcode - DW_OP_lit0;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push literal 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+ reg = opcode - DW_OP_reg0;
+ *(++sp) = registers.getRegister(reg);
+ if (log) fprintf(stderr, "push reg %d\n", reg);
+ break;
+
+ case DW_OP_regx:
+ reg = addressSpace.getULEB128(p, expressionEnd);
+ *(++sp) = registers.getRegister(reg);
+ if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue);
+ break;
+
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ reg = opcode - DW_OP_breg0;
+ svalue = addressSpace.getSLEB128(p, expressionEnd);
+ *(++sp) = registers.getRegister(reg) + svalue;
+ if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue);
+ break;
+
+ case DW_OP_bregx:
+ reg = addressSpace.getULEB128(p, expressionEnd);
+ svalue = addressSpace.getSLEB128(p, expressionEnd);
+ *(++sp) = registers.getRegister(reg) + svalue;
+ if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue);
+ break;
+
+ case DW_OP_fbreg:
+ ABORT("DW_OP_fbreg not implemented");
+ break;
+
+ case DW_OP_piece:
+ ABORT("DW_OP_piece not implemented");
+ break;
+
+ case DW_OP_deref_size:
+ // pop stack, dereference, push result
+ value = *sp--;
+ switch ( addressSpace.get8(p++) ) {
+ case 1:
+ value = addressSpace.get8(value);
+ break;
+ case 2:
+ value = addressSpace.get16(value);
+ break;
+ case 4:
+ value = addressSpace.get32(value);
+ break;
+ case 8:
+ value = addressSpace.get64(value);
+ break;
+ default:
+ ABORT("DW_OP_deref_size with bad size");
+ }
+ *(++sp) = value;
+ if (log) fprintf(stderr, "sized dereference 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_xderef_size:
+ case DW_OP_nop:
+ case DW_OP_push_object_addres:
+ case DW_OP_call2:
+ case DW_OP_call4:
+ case DW_OP_call_ref:
+ default:
+ ABORT("dwarf opcode not implemented");
+ }
+
+ }
+ if (log) fprintf(stderr, "expression evaluates to 0x%llX\n", (uint64_t)*sp);
+ return *sp;
+}
+
+
+
+//
+// x86_64 specific functions
+//
+
+template <typename A, typename R>
+int DwarfInstructions<A,R>::lastRestoreReg(const Registers_x86_64&)
+{
+ COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)DW_X86_64_RET_ADDR );
+ return DW_X86_64_RET_ADDR;
+}
+
+template <typename A, typename R>
+bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_x86_64&)
+{
+ return (regNum == DW_X86_64_RET_ADDR);
+}
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog,
+ const Registers_x86_64& registers)
+{
+ if ( prolog.cfaRegister != 0 )
+ return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset;
+ else if ( prolog.cfaExpression != 0 )
+ return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0);
+ else
+ ABORT("getCFA(): unknown location for x86_64 cfa");
+}
+
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_x86_64&)
+{
+ return UNWIND_X86_64_MODE_DWARF;
+}
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_x86&)
+{
+ return UNWIND_X86_MODE_DWARF;
+}
+
+
+
+template <typename A, typename R>
+uint32_t DwarfInstructions<A,R>::getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure)
+{
+ if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 32) ) {
+ failure = true;
+ return 0;
+ }
+ unsigned int slotIndex = regOffsetFromBaseOffset/8;
+
+ switch ( reg ) {
+ case UNW_X86_64_RBX:
+ return UNWIND_X86_64_REG_RBX << (slotIndex*3);
+ case UNW_X86_64_R12:
+ return UNWIND_X86_64_REG_R12 << (slotIndex*3);
+ case UNW_X86_64_R13:
+ return UNWIND_X86_64_REG_R13 << (slotIndex*3);
+ case UNW_X86_64_R14:
+ return UNWIND_X86_64_REG_R14 << (slotIndex*3);
+ case UNW_X86_64_R15:
+ return UNWIND_X86_64_REG_R15 << (slotIndex*3);
+ }
+
+ // invalid register
+ failure = true;
+ return 0;
+}
+
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86_64& r, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024])
+{
+ warningBuffer[0] = '\0';
+
+ if ( prolog.registerSavedTwiceInCIE == DW_X86_64_RET_ADDR ) {
+ warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ // don't create compact unwind info for unsupported dwarf kinds
+ if ( prolog.registerSavedMoreThanOnce ) {
+ strcpy(warningBuffer, "register saved more than once (might be shrink wrap)");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ if ( prolog.cfaOffsetWasNegative ) {
+ strcpy(warningBuffer, "cfa had negative offset (dwarf might contain epilog)");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ if ( prolog.spExtraArgSize != 0 ) {
+ strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ if ( prolog.sameValueUsed ) {
+ strcpy(warningBuffer, "dwarf uses DW_CFA_same_value");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+
+ // figure out which kind of frame this function uses
+ bool standardRBPframe = (
+ (prolog.cfaRegister == UNW_X86_64_RBP)
+ && (prolog.cfaRegisterOffset == 16)
+ && (prolog.savedRegisters[UNW_X86_64_RBP].location == CFI_Parser<A>::kRegisterInCFA)
+ && (prolog.savedRegisters[UNW_X86_64_RBP].value == -16) );
+ bool standardRSPframe = (prolog.cfaRegister == UNW_X86_64_RSP);
+ if ( !standardRBPframe && !standardRSPframe ) {
+ // no compact encoding for this
+ strcpy(warningBuffer, "does not use RBP or RSP based frame");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+
+ // scan which registers are saved
+ int saveRegisterCount = 0;
+ bool rbxSaved = false;
+ bool r12Saved = false;
+ bool r13Saved = false;
+ bool r14Saved = false;
+ bool r15Saved = false;
+ bool rbpSaved = false;
+ for (int i=0; i < 64; ++i) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterInCFA ) {
+ sprintf(warningBuffer, "register %d saved somewhere other that in frame", i);
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ switch (i) {
+ case UNW_X86_64_RBX:
+ rbxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R12:
+ r12Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R13:
+ r13Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R14:
+ r14Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R15:
+ r15Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_RBP:
+ rbpSaved = true;
+ ++saveRegisterCount;
+ break;
+ case DW_X86_64_RET_ADDR:
+ break;
+ default:
+ sprintf(warningBuffer, "non-standard register %d being saved in prolog", i);
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ }
+ }
+ const int64_t cfaOffsetRBX = prolog.savedRegisters[UNW_X86_64_RBX].value;
+ const int64_t cfaOffsetR12 = prolog.savedRegisters[UNW_X86_64_R12].value;
+ const int64_t cfaOffsetR13 = prolog.savedRegisters[UNW_X86_64_R13].value;
+ const int64_t cfaOffsetR14 = prolog.savedRegisters[UNW_X86_64_R14].value;
+ const int64_t cfaOffsetR15 = prolog.savedRegisters[UNW_X86_64_R15].value;
+ const int64_t cfaOffsetRBP = prolog.savedRegisters[UNW_X86_64_RBP].value;
+
+ // encode standard RBP frames
+ compact_unwind_encoding_t encoding = 0;
+ if ( standardRBPframe ) {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | rbp |
+ // +--------------+ <- rbp
+ // ~ ~
+ // +--------------+
+ // | saved reg3 |
+ // +--------------+ <- CFA - offset+16
+ // | saved reg2 |
+ // +--------------+ <- CFA - offset+8
+ // | saved reg1 |
+ // +--------------+ <- CFA - offset
+ // | |
+ // +--------------+
+ // | |
+ // <- rsp
+ //
+ encoding = UNWIND_X86_64_MODE_RBP_FRAME;
+
+ // find save location of farthest register from rbp
+ int furthestCfaOffset = 0;
+ if ( rbxSaved & (cfaOffsetRBX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetRBX;
+ if ( r12Saved & (cfaOffsetR12 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR12;
+ if ( r13Saved & (cfaOffsetR13 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR13;
+ if ( r14Saved & (cfaOffsetR14 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR14;
+ if ( r15Saved & (cfaOffsetR15 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR15;
+
+ if ( furthestCfaOffset == 0 ) {
+ // no registers saved, nothing more to encode
+ return encoding;
+ }
+
+ // add stack offset to encoding
+ int rbpOffset = furthestCfaOffset + 16;
+ int encodedOffset = rbpOffset/(-8);
+ if ( encodedOffset > 255 ) {
+ strcpy(warningBuffer, "offset of saved registers too far to encode");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET));
+
+ // add register saved from each stack location
+ bool encodingFailure = false;
+ if ( rbxSaved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_RBX, cfaOffsetRBX - furthestCfaOffset, encodingFailure);
+ if ( r12Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R12, cfaOffsetR12 - furthestCfaOffset, encodingFailure);
+ if ( r13Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R13, cfaOffsetR13 - furthestCfaOffset, encodingFailure);
+ if ( r14Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R14, cfaOffsetR14 - furthestCfaOffset, encodingFailure);
+ if ( r15Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R15, cfaOffsetR15 - furthestCfaOffset, encodingFailure);
+
+ if ( encodingFailure ){
+ strcpy(warningBuffer, "saved registers not contiguous");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+
+ return encoding;
+ }
+ else {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | saved reg1 |
+ // +--------------+ <- CFA - 16
+ // | saved reg2 |
+ // +--------------+ <- CFA - 24
+ // | saved reg3 |
+ // +--------------+ <- CFA - 32
+ // | saved reg4 |
+ // +--------------+ <- CFA - 40
+ // | saved reg5 |
+ // +--------------+ <- CFA - 48
+ // | saved reg6 |
+ // +--------------+ <- CFA - 56
+ // | |
+ // <- esp
+ //
+
+ // for RSP based frames we need to encode stack size in unwind info
+ encoding = UNWIND_X86_64_MODE_STACK_IMMD;
+ uint64_t stackValue = prolog.cfaRegisterOffset / 8;
+ uint32_t stackAdjust = 0;
+ bool immedStackSize = true;
+ const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+ if ( stackValue > stackMaxImmedValue ) {
+ // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function
+ if ( prolog.codeOffsetAtStackDecrement == 0 ) {
+ strcpy(warningBuffer, "stack size is large but stack subq instruction not found");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4;
+ try {
+ uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns);
+ stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8;
+ }
+ catch (...) {
+ strcpy(warningBuffer, "stack size is large but stack subq instruction not found");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ stackValue = functionContentAdjustStackIns - funcAddr;
+ immedStackSize = false;
+ if ( stackAdjust > 7 ) {
+ strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ encoding = UNWIND_X86_64_MODE_STACK_IND;
+ }
+
+
+ // validate that saved registers are all within 6 slots abutting return address
+ int registers[6];
+ for (int i=0; i < 6;++i)
+ registers[i] = 0;
+ if ( r15Saved ) {
+ if ( cfaOffsetR15 < -56 ) {
+ strcpy(warningBuffer, "r15 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR15+56)/8] = UNWIND_X86_64_REG_R15;
+ }
+ if ( r14Saved ) {
+ if ( cfaOffsetR14 < -56 ) {
+ strcpy(warningBuffer, "r14 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR14+56)/8] = UNWIND_X86_64_REG_R14;
+ }
+ if ( r13Saved ) {
+ if ( cfaOffsetR13 < -56 ) {
+ strcpy(warningBuffer, "r13 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR13+56)/8] = UNWIND_X86_64_REG_R13;
+ }
+ if ( r12Saved ) {
+ if ( cfaOffsetR12 < -56 ) {
+ strcpy(warningBuffer, "r12 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR12+56)/8] = UNWIND_X86_64_REG_R12;
+ }
+ if ( rbxSaved ) {
+ if ( cfaOffsetRBX < -56 ) {
+ strcpy(warningBuffer, "rbx is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetRBX+56)/8] = UNWIND_X86_64_REG_RBX;
+ }
+ if ( rbpSaved ) {
+ if ( cfaOffsetRBP < -56 ) {
+ strcpy(warningBuffer, "rbp is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetRBP+56)/8] = UNWIND_X86_64_REG_RBP;
+ }
+
+ // validate that saved registers are contiguous and abut return address on stack
+ for (int i=0; i < saveRegisterCount; ++i) {
+ if ( registers[5-i] == 0 ) {
+ strcpy(warningBuffer, "registers not save contiguously in stack");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ }
+
+ // encode register permutation
+ // the 10-bits are encoded differently depending on the number of registers saved
+ int renumregs[6];
+ for (int i=6-saveRegisterCount; i < 6; ++i) {
+ int countless = 0;
+ for (int j=6-saveRegisterCount; j < i; ++j) {
+ if ( registers[j] < registers[i] )
+ ++countless;
+ }
+ renumregs[i] = registers[i] - countless -1;
+ }
+ uint32_t permutationEncoding = 0;
+ switch ( saveRegisterCount ) {
+ case 6:
+ permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]);
+ break;
+ case 5:
+ permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]);
+ break;
+ case 4:
+ permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]);
+ break;
+ case 3:
+ permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]);
+ break;
+ case 2:
+ permutationEncoding |= (5*renumregs[4] + renumregs[5]);
+ break;
+ case 1:
+ permutationEncoding |= (renumregs[5]);
+ break;
+ }
+
+ encoding |= (stackValue << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE));
+ encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_ADJUST));
+ encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT));
+ encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION));
+ return encoding;
+ }
+}
+
+
+
+
+//
+// x86 specific functions
+//
+template <typename A, typename R>
+int DwarfInstructions<A,R>::lastRestoreReg(const Registers_x86&)
+{
+ COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)DW_X86_RET_ADDR );
+ return DW_X86_RET_ADDR;
+}
+
+template <typename A, typename R>
+bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_x86&)
+{
+ return (regNum == DW_X86_RET_ADDR);
+}
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog,
+ const Registers_x86& registers)
+{
+ if ( prolog.cfaRegister != 0 )
+ return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset;
+ else if ( prolog.cfaExpression != 0 )
+ return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0);
+ else
+ ABORT("getCFA(): unknown location for x86 cfa");
+}
+
+
+
+
+
+template <typename A, typename R>
+uint32_t DwarfInstructions<A,R>::getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure)
+{
+ if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 16) ) {
+ failure = true;
+ return 0;
+ }
+ unsigned int slotIndex = regOffsetFromBaseOffset/4;
+
+ switch ( reg ) {
+ case UNW_X86_EBX:
+ return UNWIND_X86_REG_EBX << (slotIndex*3);
+ case UNW_X86_ECX:
+ return UNWIND_X86_REG_ECX << (slotIndex*3);
+ case UNW_X86_EDX:
+ return UNWIND_X86_REG_EDX << (slotIndex*3);
+ case UNW_X86_EDI:
+ return UNWIND_X86_REG_EDI << (slotIndex*3);
+ case UNW_X86_ESI:
+ return UNWIND_X86_REG_ESI << (slotIndex*3);
+ }
+
+ // invalid register
+ failure = true;
+ return 0;
+}
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86& r, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024])
+{
+ warningBuffer[0] = '\0';
+
+ if ( prolog.registerSavedTwiceInCIE == DW_X86_RET_ADDR ) {
+ warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ // don't create compact unwind info for unsupported dwarf kinds
+ if ( prolog.registerSavedMoreThanOnce ) {
+ strcpy(warningBuffer, "register saved more than once (might be shrink wrap)");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ if ( prolog.spExtraArgSize != 0 ) {
+ strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ if ( prolog.sameValueUsed ) {
+ strcpy(warningBuffer, "dwarf uses DW_CFA_same_value");
+ return UNWIND_X86_MODE_DWARF;
+ }
+
+ // figure out which kind of frame this function uses
+ bool standardEBPframe = (
+ (prolog.cfaRegister == UNW_X86_EBP)
+ && (prolog.cfaRegisterOffset == 8)
+ && (prolog.savedRegisters[UNW_X86_EBP].location == CFI_Parser<A>::kRegisterInCFA)
+ && (prolog.savedRegisters[UNW_X86_EBP].value == -8) );
+ bool standardESPframe = (prolog.cfaRegister == UNW_X86_ESP);
+ if ( !standardEBPframe && !standardESPframe ) {
+ // no compact encoding for this
+ strcpy(warningBuffer, "does not use EBP or ESP based frame");
+ return UNWIND_X86_MODE_DWARF;
+ }
+
+ // scan which registers are saved
+ int saveRegisterCount = 0;
+ bool ebxSaved = false;
+ bool ecxSaved = false;
+ bool edxSaved = false;
+ bool esiSaved = false;
+ bool ediSaved = false;
+ bool ebpSaved = false;
+ for (int i=0; i < 64; ++i) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterInCFA ) {
+ sprintf(warningBuffer, "register %d saved somewhere other that in frame", i);
+ return UNWIND_X86_MODE_DWARF;
+ }
+ switch (i) {
+ case UNW_X86_EBX:
+ ebxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_ECX:
+ ecxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_EDX:
+ edxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_ESI:
+ esiSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_EDI:
+ ediSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_EBP:
+ ebpSaved = true;
+ ++saveRegisterCount;
+ break;
+ case DW_X86_RET_ADDR:
+ break;
+ default:
+ sprintf(warningBuffer, "non-standard register %d being saved in prolog", i);
+ return UNWIND_X86_MODE_DWARF;
+ }
+ }
+ }
+ const int32_t cfaOffsetEBX = prolog.savedRegisters[UNW_X86_EBX].value;
+ const int32_t cfaOffsetECX = prolog.savedRegisters[UNW_X86_ECX].value;
+ const int32_t cfaOffsetEDX = prolog.savedRegisters[UNW_X86_EDX].value;
+ const int32_t cfaOffsetEDI = prolog.savedRegisters[UNW_X86_EDI].value;
+ const int32_t cfaOffsetESI = prolog.savedRegisters[UNW_X86_ESI].value;
+ const int32_t cfaOffsetEBP = prolog.savedRegisters[UNW_X86_EBP].value;
+
+ // encode standard RBP frames
+ compact_unwind_encoding_t encoding = 0;
+ if ( standardEBPframe ) {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | ebp |
+ // +--------------+ <- ebp
+ // ~ ~
+ // +--------------+
+ // | saved reg3 |
+ // +--------------+ <- CFA - offset+8
+ // | saved reg2 |
+ // +--------------+ <- CFA - offset+e
+ // | saved reg1 |
+ // +--------------+ <- CFA - offset
+ // | |
+ // +--------------+
+ // | |
+ // <- esp
+ //
+ encoding = UNWIND_X86_MODE_EBP_FRAME;
+
+ // find save location of farthest register from ebp
+ int furthestCfaOffset = 0;
+ if ( ebxSaved & (cfaOffsetEBX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetEBX;
+ if ( ecxSaved & (cfaOffsetECX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetECX;
+ if ( edxSaved & (cfaOffsetEDX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetEDX;
+ if ( ediSaved & (cfaOffsetEDI < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetEDI;
+ if ( esiSaved & (cfaOffsetESI < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetESI;
+
+ if ( furthestCfaOffset == 0 ) {
+ // no registers saved, nothing more to encode
+ return encoding;
+ }
+
+ // add stack offset to encoding
+ int ebpOffset = furthestCfaOffset + 8;
+ int encodedOffset = ebpOffset/(-4);
+ if ( encodedOffset > 255 ) {
+ strcpy(warningBuffer, "offset of saved registers too far to encode");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_EBP_FRAME_OFFSET));
+
+ // add register saved from each stack location
+ bool encodingFailure = false;
+ if ( ebxSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_EBX, cfaOffsetEBX - furthestCfaOffset, encodingFailure);
+ if ( ecxSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_ECX, cfaOffsetECX - furthestCfaOffset, encodingFailure);
+ if ( edxSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_EDX, cfaOffsetEDX - furthestCfaOffset, encodingFailure);
+ if ( ediSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_EDI, cfaOffsetEDI - furthestCfaOffset, encodingFailure);
+ if ( esiSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_ESI, cfaOffsetESI - furthestCfaOffset, encodingFailure);
+
+ if ( encodingFailure ){
+ strcpy(warningBuffer, "saved registers not contiguous");
+ return UNWIND_X86_MODE_DWARF;
+ }
+
+ return encoding;
+ }
+ else {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | saved reg1 |
+ // +--------------+ <- CFA - 8
+ // | saved reg2 |
+ // +--------------+ <- CFA - 12
+ // | saved reg3 |
+ // +--------------+ <- CFA - 16
+ // | saved reg4 |
+ // +--------------+ <- CFA - 20
+ // | saved reg5 |
+ // +--------------+ <- CFA - 24
+ // | saved reg6 |
+ // +--------------+ <- CFA - 28
+ // | |
+ // <- esp
+ //
+
+ // for ESP based frames we need to encode stack size in unwind info
+ encoding = UNWIND_X86_MODE_STACK_IMMD;
+ uint64_t stackValue = prolog.cfaRegisterOffset / 4;
+ uint32_t stackAdjust = 0;
+ bool immedStackSize = true;
+ const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_FRAMELESS_STACK_SIZE);
+ if ( stackValue > stackMaxImmedValue ) {
+ // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function
+ pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4;
+ uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns);
+ stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4;
+ stackValue = functionContentAdjustStackIns - funcAddr;
+ immedStackSize = false;
+ if ( stackAdjust > 7 ) {
+ strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ encoding = UNWIND_X86_MODE_STACK_IND;
+ }
+
+
+ // validate that saved registers are all within 6 slots abutting return address
+ int registers[6];
+ for (int i=0; i < 6;++i)
+ registers[i] = 0;
+ if ( ebxSaved ) {
+ if ( cfaOffsetEBX < -28 ) {
+ strcpy(warningBuffer, "ebx is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEBX+28)/4] = UNWIND_X86_REG_EBX;
+ }
+ if ( ecxSaved ) {
+ if ( cfaOffsetECX < -28 ) {
+ strcpy(warningBuffer, "ecx is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetECX+28)/4] = UNWIND_X86_REG_ECX;
+ }
+ if ( edxSaved ) {
+ if ( cfaOffsetEDX < -28 ) {
+ strcpy(warningBuffer, "edx is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEDX+28)/4] = UNWIND_X86_REG_EDX;
+ }
+ if ( ediSaved ) {
+ if ( cfaOffsetEDI < -28 ) {
+ strcpy(warningBuffer, "edi is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEDI+28)/4] = UNWIND_X86_REG_EDI;
+ }
+ if ( esiSaved ) {
+ if ( cfaOffsetESI < -28 ) {
+ strcpy(warningBuffer, "esi is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetESI+28)/4] = UNWIND_X86_REG_ESI;
+ }
+ if ( ebpSaved ) {
+ if ( cfaOffsetEBP < -28 ) {
+ strcpy(warningBuffer, "ebp is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEBP+28)/4] = UNWIND_X86_REG_EBP;
+ }
+
+ // validate that saved registers are contiguous and abut return address on stack
+ for (int i=0; i < saveRegisterCount; ++i) {
+ if ( registers[5-i] == 0 ) {
+ strcpy(warningBuffer, "registers not save contiguously in stack");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ }
+
+ // encode register permutation
+ // the 10-bits are encoded differently depending on the number of registers saved
+ int renumregs[6];
+ for (int i=6-saveRegisterCount; i < 6; ++i) {
+ int countless = 0;
+ for (int j=6-saveRegisterCount; j < i; ++j) {
+ if ( registers[j] < registers[i] )
+ ++countless;
+ }
+ renumregs[i] = registers[i] - countless -1;
+ }
+ uint32_t permutationEncoding = 0;
+ switch ( saveRegisterCount ) {
+ case 6:
+ permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]);
+ break;
+ case 5:
+ permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]);
+ break;
+ case 4:
+ permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]);
+ break;
+ case 3:
+ permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]);
+ break;
+ case 2:
+ permutationEncoding |= (5*renumregs[4] + renumregs[5]);
+ break;
+ case 1:
+ permutationEncoding |= (renumregs[5]);
+ break;
+ }
+
+ encoding |= (stackValue << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE));
+ encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_ADJUST));
+ encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_COUNT));
+ encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION));
+ return encoding;
+ }
+}
+
+
+
+
+
+
+
+//
+// ppc specific functions
+//
+template <typename A, typename R>
+int DwarfInstructions<A,R>::lastRestoreReg(const Registers_ppc&)
+{
+ COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)UNW_PPC_SPEFSCR );
+ return UNW_PPC_SPEFSCR;
+}
+
+template <typename A, typename R>
+bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_ppc&)
+{
+ return (regNum == UNW_PPC_LR);
+}
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog,
+ const Registers_ppc& registers)
+{
+ if ( prolog.cfaRegister != 0 )
+ return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset;
+ else if ( prolog.cfaExpression != 0 )
+ return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0);
+ else
+ ABORT("getCFA(): unknown location for ppc cfa");
+}
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_ppc&)
+{
+ return UNWIND_X86_MODE_DWARF;
+}
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_ppc& r, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024])
+{
+ warningBuffer[0] = '\0';
+ return UNWIND_X86_MODE_DWARF;
+}
+
+
+
+
+} // namespace libunwind
+
+
+#endif // __DWARF_INSTRUCTIONS_HPP__
+
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// processor specific parsing of dwarf unwind instructions
+//
+
+#ifndef __DWARF_PARSER_HPP__
+#define __DWARF_PARSER_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <vector>
+
+#include "libunwind.h"
+#include "dwarf2.h"
+
+#include "AddressSpace.hpp"
+
+
+namespace libunwind {
+
+
+///
+/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
+/// See Dwarf Spec for details:
+/// http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
+///
+template <typename A>
+class CFI_Parser
+{
+public:
+ typedef typename A::pint_t pint_t;
+
+ ///
+ /// Information encoded in a CIE (Common Information Entry)
+ ///
+ struct CIE_Info {
+ pint_t cieStart;
+ pint_t cieLength;
+ pint_t cieInstructions;
+ uint8_t pointerEncoding;
+ uint8_t lsdaEncoding;
+ uint8_t personalityEncoding;
+ uint8_t personalityOffsetInCIE;
+ pint_t personality;
+ int codeAlignFactor;
+ int dataAlignFactor;
+ bool isSignalFrame;
+ bool fdesHaveAugmentationData;
+ };
+
+ ///
+ /// Information about an FDE (Frame Description Entry)
+ ///
+ struct FDE_Info {
+ pint_t fdeStart;
+ pint_t fdeLength;
+ pint_t fdeInstructions;
+ pint_t pcStart;
+ pint_t pcEnd;
+ pint_t lsda;
+ };
+
+ ///
+ /// Used by linker when parsing __eh_frame section
+ ///
+ struct FDE_Reference {
+ pint_t address;
+ uint32_t offsetInFDE;
+ uint8_t encodingOfAddress;
+ };
+ struct FDE_Atom_Info {
+ pint_t fdeAddress;
+ FDE_Reference function;
+ FDE_Reference cie;
+ FDE_Reference lsda;
+ };
+ struct CIE_Atom_Info {
+ pint_t cieAddress;
+ FDE_Reference personality;
+ };
+
+
+ ///
+ /// Information about a frame layout and registers saved determined
+ /// by "running" the dwarf FDE "instructions"
+ ///
+ enum { kMaxRegisterNumber = 120 };
+ enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA,
+ kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ;
+ struct RegisterLocation {
+ RegisterSavedWhere location;
+ int64_t value;
+ };
+ struct PrologInfo {
+ uint32_t cfaRegister;
+ int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
+ int64_t cfaExpression; // CFA = expression
+ uint32_t spExtraArgSize;
+ uint32_t codeOffsetAtStackDecrement;
+ uint8_t registerSavedTwiceInCIE;
+ bool registersInOtherRegisters;
+ bool registerSavedMoreThanOnce;
+ bool cfaOffsetWasNegative;
+ bool sameValueUsed;
+ RegisterLocation savedRegisters[kMaxRegisterNumber]; // from where to restore registers
+ };
+
+ struct PrologInfoStackEntry {
+ PrologInfoStackEntry(PrologInfoStackEntry* n, const PrologInfo& i)
+ : next(n), info(i) {}
+ PrologInfoStackEntry* next;
+ PrologInfo info;
+ };
+
+ static bool findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo);
+ static const char* decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo);
+ static bool parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results);
+ static const char* getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies);
+ static uint32_t getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength);
+
+ static const char* parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo);
+
+private:
+ static bool parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo,
+ pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results);
+};
+
+
+///
+/// Parse a FDE into a CIE_Info and an FDE_Info
+///
+template <typename A>
+const char* CFI_Parser<A>::decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo)
+{
+ pint_t p = fdeStart;
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return "FDE has zero length"; // end marker
+ uint32_t ciePointer = addressSpace.get32(p);
+ if ( ciePointer == 0 )
+ return "FDE is really a CIE"; // this is a CIE not an FDE
+ pint_t nextCFI = p + cfiLength;
+ pint_t cieStart = p-ciePointer;
+ const char* err = parseCIE(addressSpace, cieStart, cieInfo);
+ if (err != NULL)
+ return err;
+ p += 4;
+ // parse pc begin and range
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
+ // parse rest of info
+ fdeInfo->lsda = 0;
+ // check for augmentation length
+ if ( cieInfo->fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( cieInfo->lsdaEncoding != 0 ) {
+ // peek at value (without indirection). Zero means no lsda
+ pint_t lsdaStart = p;
+ if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) {
+ // reset pointer and re-parse lsda address
+ p = lsdaStart;
+ fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
+ }
+ }
+ p = endOfAug;
+ }
+ fdeInfo->fdeStart = fdeStart;
+ fdeInfo->fdeLength = nextCFI - fdeStart;
+ fdeInfo->fdeInstructions = p;
+ fdeInfo->pcStart = pcStart;
+ fdeInfo->pcEnd = pcStart+pcRange;
+ return NULL; // success
+}
+
+
+///
+/// Scan an eh_frame section to find an FDE for a pc
+///
+template <typename A>
+bool CFI_Parser<A>::findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo)
+{
+ //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
+ pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
+ const pint_t ehSectionEnd = p + sectionLength;
+ while ( p < ehSectionEnd ) {
+ pint_t currentCFI = p;
+ //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return false; // end marker
+ uint32_t id = addressSpace.get32(p);
+ if ( id == 0 ) {
+ // skip over CIEs
+ p += cfiLength;
+ }
+ else {
+ // process FDE to see if it covers pc
+ pint_t nextCFI = p + cfiLength;
+ uint32_t ciePointer = addressSpace.get32(p);
+ pint_t cieStart = p-ciePointer;
+ // validate pointer to CIE is within section
+ if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) {
+ if ( parseCIE(addressSpace, cieStart, cieInfo) == NULL ) {
+ p += 4;
+ // parse pc begin and range
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
+ //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // test if pc is within the function this FDE covers
+ if ( (pcStart < pc) && (pc <= pcStart+pcRange) ) {
+ // parse rest of info
+ fdeInfo->lsda = 0;
+ // check for augmentation length
+ if ( cieInfo->fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( cieInfo->lsdaEncoding != 0 ) {
+ // peek at value (without indirection). Zero means no lsda
+ pint_t lsdaStart = p;
+ if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) {
+ // reset pointer and re-parse lsda address
+ p = lsdaStart;
+ fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
+ }
+ }
+ p = endOfAug;
+ }
+ fdeInfo->fdeStart = currentCFI;
+ fdeInfo->fdeLength = nextCFI - currentCFI;
+ fdeInfo->fdeInstructions = p;
+ fdeInfo->pcStart = pcStart;
+ fdeInfo->pcEnd = pcStart+pcRange;
+ //fprintf(stderr, "findFDE(pc=0x%llX) found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ return true;
+ }
+ else {
+ //fprintf(stderr, "findFDE(pc=0x%llX) not found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // pc is not in begin/range, skip this FDE
+ }
+ }
+ else {
+ // malformed CIE, now augmentation describing pc range encoding
+ //fprintf(stderr, "malformed CIE\n");
+ }
+ }
+ else {
+ // malformed FDE. CIE is bad
+ //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n",
+ // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd);
+ }
+ p = nextCFI;
+ }
+ }
+ //fprintf(stderr, "findFDE(pc=0x%llX) not found\n",(uint64_t)pc);
+ return false;
+}
+
+
+
+///
+/// Extract info from a CIE
+///
+template <typename A>
+const char* CFI_Parser<A>::parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo)
+{
+ //fprintf(stderr, "parseCIE(0x%llX)\n", (long long)cie);
+ cieInfo->pointerEncoding = 0;
+ cieInfo->lsdaEncoding = 0;
+ cieInfo->personalityEncoding = 0;
+ cieInfo->personalityOffsetInCIE = 0;
+ cieInfo->personality = 0;
+ cieInfo->codeAlignFactor = 0;
+ cieInfo->dataAlignFactor = 0;
+ cieInfo->isSignalFrame = false;
+ cieInfo->fdesHaveAugmentationData = false;
+ cieInfo->cieStart = cie;
+ pint_t p = cie;
+ uint64_t cieLength = addressSpace.get32(p);
+ p += 4;
+ pint_t cieContentEnd = p + cieLength;
+ if ( cieLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cieLength = addressSpace.get64(p);
+ p += 8;
+ cieContentEnd = p + cieLength;
+ }
+ if ( cieLength == 0 )
+ return NULL;
+ // CIE ID is always 0
+ if ( addressSpace.get32(p) != 0 )
+ return "CIE ID is not zero";
+ p += 4;
+ // Version is always 1 or 3
+ uint8_t version = addressSpace.get8(p);
+ if ( (version != 1) && (version != 3) )
+ return "CIE version is not 1 or 3";
+ ++p;
+ // save start of augmentation string and find end
+ pint_t strStart = p;
+ while ( addressSpace.get8(p) != 0 )
+ ++p;
+ ++p;
+ // parse code aligment factor
+ cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd);
+ // parse data alignment factor
+ cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd);
+ // parse return address register
+ addressSpace.getULEB128(p, cieContentEnd);
+ // parse augmentation data based on augmentation string
+ const char* result = NULL;
+ if ( addressSpace.get8(strStart) == 'z' ) {
+ // parse augmentation data length
+ addressSpace.getULEB128(p, cieContentEnd);
+ for (pint_t s=strStart; addressSpace.get8(s) != '\0'; ++s) {
+ switch ( addressSpace.get8(s) ) {
+ case 'z':
+ cieInfo->fdesHaveAugmentationData = true;
+ break;
+ case 'P':
+ cieInfo->personalityEncoding = addressSpace.get8(p);
+ ++p;
+ cieInfo->personalityOffsetInCIE = p-cie;
+ cieInfo->personality = addressSpace.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
+ break;
+ case 'L':
+ cieInfo->lsdaEncoding = addressSpace.get8(p);
+ ++p;
+ break;
+ case 'R':
+ cieInfo->pointerEncoding = addressSpace.get8(p);
+ ++p;
+ break;
+ case 'S':
+ cieInfo->isSignalFrame = true;
+ break;
+ default:
+ // ignore unknown letters
+ break;
+ }
+ }
+ }
+ cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
+ cieInfo->cieInstructions = p;
+ return result;
+}
+
+
+template <typename A>
+uint32_t CFI_Parser<A>::getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength)
+{
+ uint32_t count = 0;
+ const pint_t ehSectionEnd = ehSectionStart + sectionLength;
+ for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return count; // end marker
+ ++count;
+ p += cfiLength;
+ }
+ return count;
+}
+
+
+
+template <typename A>
+const char* CFI_Parser<A>::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies)
+{
+ const pint_t ehSectionEnd = ehSectionStart + sectionLength;
+ for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
+ pint_t currentCFI = p;
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return NULL; // end marker
+ uint32_t id = addressSpace.get32(p);
+ if ( id == 0 ) {
+ // is CIE
+ CIE_Info cieInfo;
+ const char* err = parseCIE(addressSpace, currentCFI, &cieInfo);
+ if ( err != NULL )
+ return err;
+ CIE_Atom_Info entry;
+ entry.cieAddress = currentCFI;
+ entry.personality.address = cieInfo.personality;
+ entry.personality.offsetInFDE = cieInfo.personalityOffsetInCIE;
+ entry.personality.encodingOfAddress = cieInfo.personalityEncoding;
+ cies.push_back(entry);
+ p += cfiLength;
+ }
+ else {
+ // is FDE
+ FDE_Atom_Info entry;
+ entry.fdeAddress = currentCFI;
+ entry.function.address = 0;
+ entry.cie.address = 0;
+ entry.lsda.address = 0;
+ pint_t nextCFI = p + cfiLength;
+ uint32_t ciePointer = addressSpace.get32(p);
+ pint_t cieStart = p-ciePointer;
+ // validate pointer to CIE is within section
+ if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) )
+ return "FDE points to CIE outside __eh_frame section";
+ CIE_Info cieInfo;
+ const char* err = parseCIE(addressSpace, cieStart, &cieInfo);
+ if ( err != NULL )
+ return err;
+ entry.cie.address = cieStart;
+ entry.cie.offsetInFDE = p-currentCFI;
+ entry.cie.encodingOfAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel;
+ p += 4;
+ // parse pc begin and range
+ pint_t offsetOfFunctionAddress = p-currentCFI;
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F);
+ //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // test if pc is within the function this FDE covers
+ entry.function.address = pcStart;
+ entry.function.offsetInFDE = offsetOfFunctionAddress;
+ entry.function.encodingOfAddress = cieInfo.pointerEncoding;
+ // skip over augmentation length
+ if ( cieInfo.fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( (cieInfo.lsdaEncoding != 0) && (addressSpace.getP(p) != 0) ) {
+ pint_t offsetOfLSDAAddress = p-currentCFI;
+ entry.lsda.address = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding);
+ entry.lsda.offsetInFDE = offsetOfLSDAAddress;
+ entry.lsda.encodingOfAddress = cieInfo.lsdaEncoding;
+ }
+ p = endOfAug;
+ }
+ fdes.push_back(entry);
+ p = nextCFI;
+ }
+ }
+ return NULL; // success
+}
+
+
+
+///
+/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE
+///
+template <typename A>
+bool CFI_Parser<A>::parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results)
+{
+ // clear results
+ bzero(results, sizeof(PrologInfo));
+ PrologInfoStackEntry* rememberStack = NULL;
+
+ // parse CIE then FDE instructions
+ return parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart+cieInfo.cieLength,
+ cieInfo, (pint_t)(-1), rememberStack, results)
+ && parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart+fdeInfo.fdeLength,
+ cieInfo, upToPC-fdeInfo.pcStart, rememberStack, results);
+}
+
+
+///
+/// "run" the dwarf instructions
+///
+template <typename A>
+bool CFI_Parser<A>::parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo,
+ pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results)
+{
+ const bool logDwarf = false;
+ pint_t p = instructions;
+ uint32_t codeOffset = 0;
+ PrologInfo initialState = *results;
+ if ( logDwarf ) fprintf(stderr, "parseInstructions(instructions=0x%0llX)\n", (uint64_t)instructionsEnd);
+
+ // see Dwarf Spec, section 6.4.2 for details on unwind opcodes
+ while ( (p < instructionsEnd) && (codeOffset < pcoffset) ) {
+ uint64_t reg;
+ uint64_t reg2;
+ int64_t offset;
+ uint64_t length;
+ uint8_t opcode = addressSpace.get8(p);
+ uint8_t operand;
+ PrologInfoStackEntry* entry;
+ ++p;
+ switch (opcode) {
+ case DW_CFA_nop:
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_nop\n");
+ break;
+ case DW_CFA_set_loc:
+ codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_set_loc\n");
+ break;
+ case DW_CFA_advance_loc1:
+ codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
+ p += 1;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc1: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_advance_loc2:
+ codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
+ p += 2;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc2: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_advance_loc4:
+ codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
+ p += 4;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc4: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_offset_extended:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n");
+ return false;
+ }
+ if ( results->savedRegisters[reg].location != kRegisterUnused )
+ results->registerSavedMoreThanOnce = true;
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_restore_extended:
+ reg = addressSpace.getULEB128(p, instructionsEnd);;
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg] = initialState.savedRegisters[reg];
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg);
+ break;
+ case DW_CFA_undefined:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_undefined dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterUnused;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg);
+ break;
+ case DW_CFA_same_value:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_same_value dwarf unwind, reg too big\n");
+ return false;
+ }
+ // <rdar://problem/8456377> DW_CFA_same_value unsupported
+ // "same value" means register was stored in frame, but its current
+ // value has not changed, so no need to restore from frame.
+ // We model this as if the register was never saved.
+ results->savedRegisters[reg].location = kRegisterUnused;
+ // set flag to disable conversion to compact unwind
+ results->sameValueUsed = true;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg);
+ break;
+ case DW_CFA_register:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ reg2 = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg too big\n");
+ return false;
+ }
+ if ( reg2 > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg2 too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterInRegister;
+ results->savedRegisters[reg].value = reg2;
+ // set flag to disable conversion to compact unwind
+ results->registersInOtherRegisters = true;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2);
+ break;
+ case DW_CFA_remember_state:
+ // avoid operator new, because that would be an upward dependency
+ entry = (PrologInfoStackEntry*)malloc(sizeof(PrologInfoStackEntry));
+ if ( entry != NULL ) {
+ entry->next = rememberStack;
+ entry->info = *results;
+ rememberStack = entry;
+ }
+ else {
+ return false;
+ }
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_remember_state\n");
+ break;
+ case DW_CFA_restore_state:
+ if ( rememberStack != NULL ) {
+ PrologInfoStackEntry* top = rememberStack;
+ *results = top->info;
+ rememberStack = top->next;
+ free((char*)top);
+ }
+ else {
+ return false;
+ }
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_state\n");
+ break;
+ case DW_CFA_def_cfa:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->cfaRegister = reg;
+ results->cfaRegisterOffset = offset;
+ if ( offset > 0x80000000 )
+ results->cfaOffsetWasNegative = true;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_def_cfa_register:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->cfaRegister = reg;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg);
+ break;
+ case DW_CFA_def_cfa_offset:
+ results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd);
+ results->codeOffsetAtStackDecrement = codeOffset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset);
+ break;
+ case DW_CFA_def_cfa_expression:
+ results->cfaRegister = 0;
+ results->cfaExpression = p;
+ length = addressSpace.getULEB128(p, instructionsEnd);
+ p += length;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n",
+ results->cfaExpression, length);
+ break;
+ case DW_CFA_expression:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_expression dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterAtExpression;
+ results->savedRegisters[reg].value = p;
+ length = addressSpace.getULEB128(p, instructionsEnd);
+ p += length;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n",
+ reg, results->savedRegisters[reg].value, length);
+ break;
+ case DW_CFA_offset_extended_sf:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n");
+ return false;
+ }
+ offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( results->savedRegisters[reg].location != kRegisterUnused )
+ results->registerSavedMoreThanOnce = true;
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_def_cfa_sf:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->cfaRegister = reg;
+ results->cfaRegisterOffset = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_def_cfa_offset_sf:
+ results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ results->codeOffsetAtStackDecrement = codeOffset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset);
+ break;
+ case DW_CFA_val_offset:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, offset);
+ break;
+ case DW_CFA_val_offset_sf:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n");
+ return false;
+ }
+ offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, offset);
+ break;
+ case DW_CFA_val_expression:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_val_expression dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterIsExpression;
+ results->savedRegisters[reg].value = p;
+ length = addressSpace.getULEB128(p, instructionsEnd);
+ p += length;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n",
+ reg, results->savedRegisters[reg].value, length);
+ break;
+ case DW_CFA_GNU_args_size:
+ offset = addressSpace.getULEB128(p, instructionsEnd);
+ results->spExtraArgSize = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", offset);
+ break;
+ case DW_CFA_GNU_negative_offset_extended:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf unwind, reg too big\n");
+ return false;
+ }
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( results->savedRegisters[reg].location != kRegisterUnused )
+ results->registerSavedMoreThanOnce = true;
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = -offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset);
+ break;
+ default:
+ operand = opcode & 0x3F;
+ switch ( opcode & 0xC0 ) {
+ case DW_CFA_offset:
+ reg = operand;
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( results->savedRegisters[reg].location != kRegisterUnused ) {
+ // look for idiom of PC saved twice in CIE to mean disable compact unwind encoding
+ if ( (pcoffset == (pint_t)(-1))
+ && (results->savedRegisters[reg].location == kRegisterInCFA)
+ && (results->savedRegisters[reg].value == offset) )
+ results->registerSavedTwiceInCIE = reg;
+ else
+ results->registerSavedMoreThanOnce = true;
+ }
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, offset);
+ break;
+ case DW_CFA_advance_loc:
+ codeOffset += operand * cieInfo.codeAlignFactor;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_restore:
+ // <rdar://problem/7503075> Python crashes when handling an exception thrown by an obj-c object
+ // libffi uses DW_CFA_restore in the middle of some custom dwarf, so it is not a good epilog flag
+ //return true; // gcc-4.5 starts the epilog with this
+ reg = operand;
+ results->savedRegisters[reg] = initialState.savedRegisters[reg];
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg);
+ break;
+ default:
+ if ( logDwarf ) fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+} // namespace libunwind
+
+
+#endif // __DWARF_PARSER_HPP__
+
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#ifndef INTERNAL_MACROS_H
+#define INTERNAL_MACROS_H
+
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern void __assert_rtn(const char *, const char *, int, const char *) __attribute__((noreturn));
+#ifdef __cplusplus
+}
+#endif
+
+#define UNW_STEP_SUCCESS 1
+#define UNW_STEP_END 0
+
+
+struct v128 { unsigned int vec[4]; };
+
+
+#define EXPORT __attribute__((visibility("default")))
+
+#define COMPILE_TIME_ASSERT( expr ) \
+ extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) );
+
+#define ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg)
+
+#if NDEBUG
+ #define DEBUG_MESSAGE(msg, ...)
+ #define DEBUG_PRINT_API(msg, ...)
+ #define DEBUG_PRINT_UNWINDING_TEST 0
+ #define DEBUG_PRINT_UNWINDING(msg, ...)
+ #define DEBUG_LOG_NON_ZERO(x) x;
+ #define INITIALIZE_DEBUG_PRINT_API
+ #define INITIALIZE_DEBUG_PRINT_UNWINDING
+#else
+ #define DEBUG_MESSAGE(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__)
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+ extern bool logAPIs();
+ extern bool logUnwinding();
+ #ifdef __cplusplus
+ }
+ #endif
+ #define DEBUG_LOG_NON_ZERO(x) { int _err = x; if ( _err != 0 ) fprintf(stderr, "libuwind: " #x "=%d in %s", _err, __FUNCTION__); }
+ #define DEBUG_PRINT_API(msg, ...) do { if ( logAPIs() ) fprintf(stderr, msg, __VA_ARGS__); } while(0)
+ #define DEBUG_PRINT_UNWINDING(msg, ...) do { if ( logUnwinding() ) fprintf(stderr, msg, __VA_ARGS__); } while(0)
+ #define DEBUG_PRINT_UNWINDING_TEST logUnwinding()
+ #define INITIALIZE_DEBUG_PRINT_API bool logAPIs() { static bool log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); return log; }
+ #define INITIALIZE_DEBUG_PRINT_UNWINDING bool logUnwinding() { static bool log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); return log; }
+#endif
+
+
+// note hack for <rdar://problem/6175741>
+// Once libgcc_s.dylib vectors to libSystem, then we can remove the $ld$hide$os10.6$ lines
+#if __ppc__
+ #define NOT_HERE_BEFORE_10_6(sym) \
+ extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0;
+ #define NEVER_HERE(sym) \
+ extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \
+ extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0;
+#else
+ #define NOT_HERE_BEFORE_10_6(sym) \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0;
+ #define NEVER_HERE(sym) \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \
+ extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0;
+#endif
+
+
+
+#endif // INTERNAL_MACROS_H
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2007-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@
+ */
+
+//
+// C++ interface to lower levels of libuwind
+//
+
+#ifndef __REGISTERS_HPP__
+#define __REGISTERS_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/loader.h>
+#include <mach-o/getsect.h>
+#include <mach/i386/thread_status.h>
+
+#include "libunwind.h"
+#include "InternalMacros.h"
+
+namespace libunwind {
+
+
+///
+/// Registers_x86 holds the register state of a thread in a 32-bit intel process.
+///
+class Registers_x86
+{
+public:
+ Registers_x86();
+ Registers_x86(const void* registers);
+
+ bool validRegister(int num) const;
+ uint32_t getRegister(int num) const;
+ void setRegister(int num, uint32_t value);
+ bool validFloatRegister(int num) const { return false; }
+ double getFloatRegister(int num) const;
+ void setFloatRegister(int num, double value);
+ bool validVectorRegister(int num) const { return false; }
+ v128 getVectorRegister(int num) const;
+ void setVectorRegister(int num, v128 value);
+ const char* getRegisterName(int num);
+ void jumpto();
+
+ uint32_t getSP() const { return fRegisters.__esp; }
+ void setSP(uint32_t value) { fRegisters.__esp = value; }
+ uint32_t getIP() const { return fRegisters.__eip; }
+ void setIP(uint32_t value) { fRegisters.__eip = value; }
+ uint32_t getEBP() const { return fRegisters.__ebp; }
+ void setEBP(uint32_t value) { fRegisters.__ebp = value; }
+ uint32_t getEBX() const { return fRegisters.__ebx; }
+ void setEBX(uint32_t value) { fRegisters.__ebx = value; }
+ uint32_t getECX() const { return fRegisters.__ecx; }
+ void setECX(uint32_t value) { fRegisters.__ecx = value; }
+ uint32_t getEDX() const { return fRegisters.__edx; }
+ void setEDX(uint32_t value) { fRegisters.__edx = value; }
+ uint32_t getESI() const { return fRegisters.__esi; }
+ void setESI(uint32_t value) { fRegisters.__esi = value; }
+ uint32_t getEDI() const { return fRegisters.__edi; }
+ void setEDI(uint32_t value) { fRegisters.__edi = value; }
+
+private:
+ i386_thread_state_t fRegisters;
+};
+
+inline Registers_x86::Registers_x86(const void* registers)
+{
+ COMPILE_TIME_ASSERT( sizeof(Registers_x86) < sizeof(unw_context_t) );
+ fRegisters = *((i386_thread_state_t*)registers);
+}
+
+inline Registers_x86::Registers_x86()
+{
+ bzero(&fRegisters, sizeof(fRegisters));
+}
+
+
+inline bool Registers_x86::validRegister(int regNum) const
+{
+ if ( regNum == UNW_REG_IP )
+ return true;
+ if ( regNum == UNW_REG_SP )
+ return true;
+ if ( regNum < 0 )
+ return false;
+ if ( regNum > 7 )
+ return false;
+ return true;
+}
+
+inline uint32_t Registers_x86::getRegister(int regNum) const
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return fRegisters.__eip;
+ case UNW_REG_SP:
+ return fRegisters.__esp;
+ case UNW_X86_EAX:
+ return fRegisters.__eax;
+ case UNW_X86_ECX:
+ return fRegisters.__ecx;
+ case UNW_X86_EDX:
+ return fRegisters.__edx;
+ case UNW_X86_EBX:
+ return fRegisters.__ebx;
+ case UNW_X86_EBP:
+ return fRegisters.__ebp;
+ case UNW_X86_ESP:
+ return fRegisters.__esp;
+ case UNW_X86_ESI:
+ return fRegisters.__esi;
+ case UNW_X86_EDI:
+ return fRegisters.__edi;
+ }
+ ABORT("unsupported x86 register");
+}
+
+inline void Registers_x86::setRegister(int regNum, uint32_t value)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ fRegisters.__eip = value;
+ return;
+ case UNW_REG_SP:
+ fRegisters.__esp = value;
+ return;
+ case UNW_X86_EAX:
+ fRegisters.__eax = value;
+ return;
+ case UNW_X86_ECX:
+ fRegisters.__ecx = value;
+ return;
+ case UNW_X86_EDX:
+ fRegisters.__edx = value;
+ return;
+ case UNW_X86_EBX:
+ fRegisters.__ebx = value;
+ return;
+ case UNW_X86_EBP:
+ fRegisters.__ebp = value;
+ return;
+ case UNW_X86_ESP:
+ fRegisters.__esp = value;
+ return;
+ case UNW_X86_ESI:
+ fRegisters.__esi = value;
+ return;
+ case UNW_X86_EDI:
+ fRegisters.__edi = value;
+ return;
+ }
+ ABORT("unsupported x86 register");
+}
+
+inline const char* Registers_x86::getRegisterName(int regNum)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return "ip";
+ case UNW_REG_SP:
+ return "esp";
+ case UNW_X86_EAX:
+ return "eax";
+ case UNW_X86_ECX:
+ return "ecx";
+ case UNW_X86_EDX:
+ return "edx";
+ case UNW_X86_EBX:
+ return "ebx";
+ case UNW_X86_EBP:
+ return "ebp";
+ case UNW_X86_ESP:
+ return "esp";
+ case UNW_X86_ESI:
+ return "esi";
+ case UNW_X86_EDI:
+ return "edi";
+ default:
+ return "unknown register";
+ }
+}
+
+inline double Registers_x86::getFloatRegister(int num) const
+{
+ ABORT("no x86 float registers");
+}
+
+inline void Registers_x86::setFloatRegister(int num, double value)
+{
+ ABORT("no x86 float registers");
+}
+
+inline v128 Registers_x86::getVectorRegister(int num) const
+{
+ ABORT("no x86 vector registers");
+}
+
+inline void Registers_x86::setVectorRegister(int num, v128 value)
+{
+ ABORT("no x86 vector registers");
+}
+
+
+
+
+///
+/// Registers_x86_64 holds the register state of a thread in a 64-bit intel process.
+///
+class Registers_x86_64
+{
+public:
+ Registers_x86_64();
+ Registers_x86_64(const void* registers);
+
+ bool validRegister(int num) const;
+ uint64_t getRegister(int num) const;
+ void setRegister(int num, uint64_t value);
+ bool validFloatRegister(int num) const{ return false; }
+ double getFloatRegister(int num) const;
+ void setFloatRegister(int num, double value);
+ bool validVectorRegister(int num) const { return false; }
+ v128 getVectorRegister(int num) const;
+ void setVectorRegister(int num, v128 value);
+ const char* getRegisterName(int num);
+ void jumpto();
+ uint64_t getSP() const { return fRegisters.__rsp; }
+ void setSP(uint64_t value) { fRegisters.__rsp = value; }
+ uint64_t getIP() const { return fRegisters.__rip; }
+ void setIP(uint64_t value) { fRegisters.__rip = value; }
+ uint64_t getRBP() const { return fRegisters.__rbp; }
+ void setRBP(uint64_t value) { fRegisters.__rbp = value; }
+ uint64_t getRBX() const { return fRegisters.__rbx; }
+ void setRBX(uint64_t value) { fRegisters.__rbx = value; }
+ uint64_t getR12() const { return fRegisters.__r12; }
+ void setR12(uint64_t value) { fRegisters.__r12 = value; }
+ uint64_t getR13() const { return fRegisters.__r13; }
+ void setR13(uint64_t value) { fRegisters.__r13 = value; }
+ uint64_t getR14() const { return fRegisters.__r14; }
+ void setR14(uint64_t value) { fRegisters.__r14 = value; }
+ uint64_t getR15() const { return fRegisters.__r15; }
+ void setR15(uint64_t value) { fRegisters.__r15 = value; }
+private:
+ x86_thread_state64_t fRegisters;
+};
+
+inline Registers_x86_64::Registers_x86_64(const void* registers)
+{
+ COMPILE_TIME_ASSERT( sizeof(Registers_x86_64) < sizeof(unw_context_t) );
+ fRegisters = *((x86_thread_state64_t*)registers);
+}
+
+inline Registers_x86_64::Registers_x86_64()
+{
+ bzero(&fRegisters, sizeof(fRegisters));
+}
+
+
+inline bool Registers_x86_64::validRegister(int regNum) const
+{
+ if ( regNum == UNW_REG_IP )
+ return true;
+ if ( regNum == UNW_REG_SP )
+ return true;
+ if ( regNum < 0 )
+ return false;
+ if ( regNum > 15 )
+ return false;
+ return true;
+}
+
+inline uint64_t Registers_x86_64::getRegister(int regNum) const
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return fRegisters.__rip;
+ case UNW_REG_SP:
+ return fRegisters.__rsp;
+ case UNW_X86_64_RAX:
+ return fRegisters.__rax;
+ case UNW_X86_64_RDX:
+ return fRegisters.__rdx;
+ case UNW_X86_64_RCX:
+ return fRegisters.__rcx;
+ case UNW_X86_64_RBX:
+ return fRegisters.__rbx;
+ case UNW_X86_64_RSI:
+ return fRegisters.__rsi;
+ case UNW_X86_64_RDI:
+ return fRegisters.__rdi;
+ case UNW_X86_64_RBP:
+ return fRegisters.__rbp;
+ case UNW_X86_64_RSP:
+ return fRegisters.__rsp;
+ case UNW_X86_64_R8:
+ return fRegisters.__r8;
+ case UNW_X86_64_R9:
+ return fRegisters.__r9;
+ case UNW_X86_64_R10:
+ return fRegisters.__r10;
+ case UNW_X86_64_R11:
+ return fRegisters.__r11;
+ case UNW_X86_64_R12:
+ return fRegisters.__r12;
+ case UNW_X86_64_R13:
+ return fRegisters.__r13;
+ case UNW_X86_64_R14:
+ return fRegisters.__r14;
+ case UNW_X86_64_R15:
+ return fRegisters.__r15;
+ }
+ ABORT("unsupported x86_64 register");
+}
+
+inline void Registers_x86_64::setRegister(int regNum, uint64_t value)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ fRegisters.__rip = value;
+ return;
+ case UNW_REG_SP:
+ fRegisters.__rsp = value;
+ return;
+ case UNW_X86_64_RAX:
+ fRegisters.__rax = value;
+ return;
+ case UNW_X86_64_RDX:
+ fRegisters.__rdx = value;
+ return;
+ case UNW_X86_64_RCX:
+ fRegisters.__rcx = value;
+ return;
+ case UNW_X86_64_RBX:
+ fRegisters.__rbx = value;
+ return;
+ case UNW_X86_64_RSI:
+ fRegisters.__rsi = value;
+ return;
+ case UNW_X86_64_RDI:
+ fRegisters.__rdi = value;
+ return;
+ case UNW_X86_64_RBP:
+ fRegisters.__rbp = value;
+ return;
+ case UNW_X86_64_RSP:
+ fRegisters.__rsp = value;
+ return;
+ case UNW_X86_64_R8:
+ fRegisters.__r8 = value;
+ return;
+ case UNW_X86_64_R9:
+ fRegisters.__r9 = value;
+ return;
+ case UNW_X86_64_R10:
+ fRegisters.__r10 = value;
+ return;
+ case UNW_X86_64_R11:
+ fRegisters.__r11 = value;
+ return;
+ case UNW_X86_64_R12:
+ fRegisters.__r12 = value;
+ return;
+ case UNW_X86_64_R13:
+ fRegisters.__r13 = value;
+ return;
+ case UNW_X86_64_R14:
+ fRegisters.__r14 = value;
+ return;
+ case UNW_X86_64_R15:
+ fRegisters.__r15 = value;
+ return;
+ }
+ ABORT("unsupported x86_64 register");
+}
+
+inline const char* Registers_x86_64::getRegisterName(int regNum)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return "rip";
+ case UNW_REG_SP:
+ return "rsp";
+ case UNW_X86_64_RAX:
+ return "rax";
+ case UNW_X86_64_RDX:
+ return "rdx";
+ case UNW_X86_64_RCX:
+ return "rcx";
+ case UNW_X86_64_RBX:
+ return "rbx";
+ case UNW_X86_64_RSI:
+ return "rsi";
+ case UNW_X86_64_RDI:
+ return "rdi";
+ case UNW_X86_64_RBP:
+ return "rbp";
+ case UNW_X86_64_RSP:
+ return "rsp";
+ case UNW_X86_64_R8:
+ return "r8";
+ case UNW_X86_64_R9:
+ return "r9";
+ case UNW_X86_64_R10:
+ return "r10";
+ case UNW_X86_64_R11:
+ return "r11";
+ case UNW_X86_64_R12:
+ return "r12";
+ case UNW_X86_64_R13:
+ return "r13";
+ case UNW_X86_64_R14:
+ return "r14";
+ case UNW_X86_64_R15:
+ return "r15";
+ default:
+ return "unknown register";
+ }
+}
+
+double Registers_x86_64::getFloatRegister(int num) const
+{
+ ABORT("no x86_64 float registers");
+}
+
+void Registers_x86_64::setFloatRegister(int num, double value)
+{
+ ABORT("no x86_64 float registers");
+}
+
+inline v128 Registers_x86_64::getVectorRegister(int num) const
+{
+ ABORT("no x86_64 vector registers");
+}
+
+inline void Registers_x86_64::setVectorRegister(int num, v128 value)
+{
+ ABORT("no x86_64 vector registers");
+}
+
+
+///
+/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC process.
+///
+class Registers_ppc
+{
+public:
+ Registers_ppc();
+ Registers_ppc(const void* registers);
+
+ bool validRegister(int num) const;
+ uint32_t getRegister(int num) const;
+ void setRegister(int num, uint32_t value);
+ bool validFloatRegister(int num) const;
+ double getFloatRegister(int num) const;
+ void setFloatRegister(int num, double value);
+ bool validVectorRegister(int num) const;
+ v128 getVectorRegister(int num) const;
+ void setVectorRegister(int num, v128 value);
+ void jumpto();
+ const char* getRegisterName(int num);
+ uint64_t getSP() const { return fRegisters.__r1; }
+ void setSP(uint64_t value) { fRegisters.__r1 = value; }
+ uint64_t getIP() const { return fRegisters.__srr0; }
+ void setIP(uint64_t value) { fRegisters.__srr0 = value; }
+private:
+ struct ppc_thread_state_t
+ {
+ unsigned int __srr0; /* Instruction address register (PC) */
+ unsigned int __srr1; /* Machine state register (supervisor) */
+ unsigned int __r0;
+ unsigned int __r1;
+ unsigned int __r2;
+ unsigned int __r3;
+ unsigned int __r4;
+ unsigned int __r5;
+ unsigned int __r6;
+ unsigned int __r7;
+ unsigned int __r8;
+ unsigned int __r9;
+ unsigned int __r10;
+ unsigned int __r11;
+ unsigned int __r12;
+ unsigned int __r13;
+ unsigned int __r14;
+ unsigned int __r15;
+ unsigned int __r16;
+ unsigned int __r17;
+ unsigned int __r18;
+ unsigned int __r19;
+ unsigned int __r20;
+ unsigned int __r21;
+ unsigned int __r22;
+ unsigned int __r23;
+ unsigned int __r24;
+ unsigned int __r25;
+ unsigned int __r26;
+ unsigned int __r27;
+ unsigned int __r28;
+ unsigned int __r29;
+ unsigned int __r30;
+ unsigned int __r31;
+ unsigned int __cr; /* Condition register */
+ unsigned int __xer; /* User's integer exception register */
+ unsigned int __lr; /* Link register */
+ unsigned int __ctr; /* Count register */
+ unsigned int __mq; /* MQ register (601 only) */
+ unsigned int __vrsave; /* Vector Save Register */
+ };
+
+ struct ppc_float_state_t
+ {
+ double __fpregs[32];
+
+ unsigned int __fpscr_pad; /* fpscr is 64 bits, 32 bits of rubbish */
+ unsigned int __fpscr; /* floating point status register */
+ };
+
+ ppc_thread_state_t fRegisters;
+ ppc_float_state_t fFloatRegisters;
+ v128 fVectorRegisters[32]; // offset 424
+};
+
+
+
+inline Registers_ppc::Registers_ppc(const void* registers)
+{
+ COMPILE_TIME_ASSERT( sizeof(Registers_ppc) < sizeof(unw_context_t) );
+ fRegisters = *((ppc_thread_state_t*)registers);
+ fFloatRegisters = *((ppc_float_state_t*)((char*)registers+160));
+ memcpy(fVectorRegisters, ((char*)registers+424), sizeof(fVectorRegisters));
+}
+
+inline Registers_ppc::Registers_ppc()
+{
+ bzero(&fRegisters, sizeof(fRegisters));
+ bzero(&fFloatRegisters, sizeof(fFloatRegisters));
+ bzero(&fVectorRegisters, sizeof(fVectorRegisters));
+}
+
+
+inline bool Registers_ppc::validRegister(int regNum) const
+{
+ if ( regNum == UNW_REG_IP )
+ return true;
+ if ( regNum == UNW_REG_SP )
+ return true;
+ if ( regNum == UNW_PPC_VRSAVE )
+ return true;
+ if ( regNum < 0 )
+ return false;
+ if ( regNum <= UNW_PPC_R31 )
+ return true;
+ if ( regNum == UNW_PPC_MQ )
+ return true;
+ if ( regNum == UNW_PPC_LR )
+ return true;
+ if ( regNum == UNW_PPC_CTR )
+ return true;
+ if ( (UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7) )
+ return true;
+ return false;
+}
+
+
+inline uint32_t Registers_ppc::getRegister(int regNum) const
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return fRegisters.__srr0;
+ case UNW_REG_SP:
+ return fRegisters.__r1;
+ case UNW_PPC_R0:
+ return fRegisters.__r0;
+ case UNW_PPC_R1:
+ return fRegisters.__r1;
+ case UNW_PPC_R2:
+ return fRegisters.__r2;
+ case UNW_PPC_R3:
+ return fRegisters.__r3;
+ case UNW_PPC_R4:
+ return fRegisters.__r4;
+ case UNW_PPC_R5:
+ return fRegisters.__r5;
+ case UNW_PPC_R6:
+ return fRegisters.__r6;
+ case UNW_PPC_R7:
+ return fRegisters.__r7;
+ case UNW_PPC_R8:
+ return fRegisters.__r8;
+ case UNW_PPC_R9:
+ return fRegisters.__r9;
+ case UNW_PPC_R10:
+ return fRegisters.__r10;
+ case UNW_PPC_R11:
+ return fRegisters.__r11;
+ case UNW_PPC_R12:
+ return fRegisters.__r12;
+ case UNW_PPC_R13:
+ return fRegisters.__r13;
+ case UNW_PPC_R14:
+ return fRegisters.__r14;
+ case UNW_PPC_R15:
+ return fRegisters.__r15;
+ case UNW_PPC_R16:
+ return fRegisters.__r16;
+ case UNW_PPC_R17:
+ return fRegisters.__r17;
+ case UNW_PPC_R18:
+ return fRegisters.__r18;
+ case UNW_PPC_R19:
+ return fRegisters.__r19;
+ case UNW_PPC_R20:
+ return fRegisters.__r20;
+ case UNW_PPC_R21:
+ return fRegisters.__r21;
+ case UNW_PPC_R22:
+ return fRegisters.__r22;
+ case UNW_PPC_R23:
+ return fRegisters.__r23;
+ case UNW_PPC_R24:
+ return fRegisters.__r24;
+ case UNW_PPC_R25:
+ return fRegisters.__r25;
+ case UNW_PPC_R26:
+ return fRegisters.__r26;
+ case UNW_PPC_R27:
+ return fRegisters.__r27;
+ case UNW_PPC_R28:
+ return fRegisters.__r28;
+ case UNW_PPC_R29:
+ return fRegisters.__r29;
+ case UNW_PPC_R30:
+ return fRegisters.__r30;
+ case UNW_PPC_R31:
+ return fRegisters.__r31;
+ case UNW_PPC_LR:
+ return fRegisters.__lr;
+ case UNW_PPC_CR0:
+ return (fRegisters.__cr & 0xF0000000);
+ case UNW_PPC_CR1:
+ return (fRegisters.__cr & 0x0F000000);
+ case UNW_PPC_CR2:
+ return (fRegisters.__cr & 0x00F00000);
+ case UNW_PPC_CR3:
+ return (fRegisters.__cr & 0x000F0000);
+ case UNW_PPC_CR4:
+ return (fRegisters.__cr & 0x0000F000);
+ case UNW_PPC_CR5:
+ return (fRegisters.__cr & 0x00000F00);
+ case UNW_PPC_CR6:
+ return (fRegisters.__cr & 0x000000F0);
+ case UNW_PPC_CR7:
+ return (fRegisters.__cr & 0x0000000F);
+ case UNW_PPC_VRSAVE:
+ return fRegisters.__vrsave;
+ }
+ ABORT("unsupported ppc register");
+}
+
+
+inline void Registers_ppc::setRegister(int regNum, uint32_t value)
+{
+ //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value);
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ fRegisters.__srr0 = value;
+ return;
+ case UNW_REG_SP:
+ fRegisters.__r1 = value;
+ return;
+ case UNW_PPC_R0:
+ fRegisters.__r0 = value;
+ return;
+ case UNW_PPC_R1:
+ fRegisters.__r1 = value;
+ return;
+ case UNW_PPC_R2:
+ fRegisters.__r2 = value;
+ return;
+ case UNW_PPC_R3:
+ fRegisters.__r3 = value;
+ return;
+ case UNW_PPC_R4:
+ fRegisters.__r4 = value;
+ return;
+ case UNW_PPC_R5:
+ fRegisters.__r5 = value;
+ return;
+ case UNW_PPC_R6:
+ fRegisters.__r6 = value;
+ return;
+ case UNW_PPC_R7:
+ fRegisters.__r7 = value;
+ return;
+ case UNW_PPC_R8:
+ fRegisters.__r8 = value;
+ return;
+ case UNW_PPC_R9:
+ fRegisters.__r9 = value;
+ return;
+ case UNW_PPC_R10:
+ fRegisters.__r10 = value;
+ return;
+ case UNW_PPC_R11:
+ fRegisters.__r11 = value;
+ return;
+ case UNW_PPC_R12:
+ fRegisters.__r12 = value;
+ return;
+ case UNW_PPC_R13:
+ fRegisters.__r13 = value;
+ return;
+ case UNW_PPC_R14:
+ fRegisters.__r14 = value;
+ return;
+ case UNW_PPC_R15:
+ fRegisters.__r15 = value;
+ return;
+ case UNW_PPC_R16:
+ fRegisters.__r16 = value;
+ return;
+ case UNW_PPC_R17:
+ fRegisters.__r17 = value;
+ return;
+ case UNW_PPC_R18:
+ fRegisters.__r18 = value;
+ return;
+ case UNW_PPC_R19:
+ fRegisters.__r19 = value;
+ return;
+ case UNW_PPC_R20:
+ fRegisters.__r20 = value;
+ return;
+ case UNW_PPC_R21:
+ fRegisters.__r21 = value;
+ return;
+ case UNW_PPC_R22:
+ fRegisters.__r22 = value;
+ return;
+ case UNW_PPC_R23:
+ fRegisters.__r23 = value;
+ return;
+ case UNW_PPC_R24:
+ fRegisters.__r24 = value;
+ return;
+ case UNW_PPC_R25:
+ fRegisters.__r25 = value;
+ return;
+ case UNW_PPC_R26:
+ fRegisters.__r26 = value;
+ return;
+ case UNW_PPC_R27:
+ fRegisters.__r27 = value;
+ return;
+ case UNW_PPC_R28:
+ fRegisters.__r28 = value;
+ return;
+ case UNW_PPC_R29:
+ fRegisters.__r29 = value;
+ return;
+ case UNW_PPC_R30:
+ fRegisters.__r30 = value;
+ return;
+ case UNW_PPC_R31:
+ fRegisters.__r31 = value;
+ return;
+ case UNW_PPC_MQ:
+ fRegisters.__mq = value;
+ return;
+ case UNW_PPC_LR:
+ fRegisters.__lr = value;
+ return;
+ case UNW_PPC_CTR:
+ fRegisters.__ctr = value;
+ return;
+ case UNW_PPC_CR0:
+ fRegisters.__cr &= 0x0FFFFFFF;
+ fRegisters.__cr |= (value & 0xF0000000);
+ return;
+ case UNW_PPC_CR1:
+ fRegisters.__cr &= 0xF0FFFFFF;
+ fRegisters.__cr |= (value & 0x0F000000);
+ return;
+ case UNW_PPC_CR2:
+ fRegisters.__cr &= 0xFF0FFFFF;
+ fRegisters.__cr |= (value & 0x00F00000);
+ return;
+ case UNW_PPC_CR3:
+ fRegisters.__cr &= 0xFFF0FFFF;
+ fRegisters.__cr |= (value & 0x000F0000);
+ return;
+ case UNW_PPC_CR4:
+ fRegisters.__cr &= 0xFFFF0FFF;
+ fRegisters.__cr |= (value & 0x0000F000);
+ return;
+ case UNW_PPC_CR5:
+ fRegisters.__cr &= 0xFFFFF0FF;
+ fRegisters.__cr |= (value & 0x00000F00);
+ return;
+ case UNW_PPC_CR6:
+ fRegisters.__cr &= 0xFFFFFF0F;
+ fRegisters.__cr |= (value & 0x000000F0);
+ return;
+ case UNW_PPC_CR7:
+ fRegisters.__cr &= 0xFFFFFFF0;
+ fRegisters.__cr |= (value & 0x0000000F);
+ return;
+ case UNW_PPC_VRSAVE:
+ fRegisters.__vrsave = value;
+ return;
+ // not saved
+ return;
+ case UNW_PPC_XER:
+ fRegisters.__xer = value;
+ return;
+ case UNW_PPC_AP:
+ case UNW_PPC_VSCR:
+ case UNW_PPC_SPEFSCR:
+ // not saved
+ return;
+ }
+ ABORT("unsupported ppc register");
+}
+
+inline bool Registers_ppc::validFloatRegister(int regNum) const
+{
+ if ( regNum < UNW_PPC_F0 )
+ return false;
+ if ( regNum > UNW_PPC_F31 )
+ return false;
+ return true;
+}
+
+inline double Registers_ppc::getFloatRegister(int regNum) const
+{
+ assert(validFloatRegister(regNum));
+ return fFloatRegisters.__fpregs[regNum-UNW_PPC_F0];
+}
+
+inline void Registers_ppc::setFloatRegister(int regNum, double value)
+{
+ //fprintf(stderr, "Registers_ppc::setFloatRegister(%d, %g))\n", regNum, value);
+ assert(validFloatRegister(regNum));
+ fFloatRegisters.__fpregs[regNum-UNW_PPC_F0] = value;
+}
+
+
+inline bool Registers_ppc::validVectorRegister(int regNum) const
+{
+ if ( regNum < UNW_PPC_V0 )
+ return false;
+ if ( regNum > UNW_PPC_V31 )
+ return false;
+ return true;
+}
+
+v128 Registers_ppc::getVectorRegister(int regNum) const
+{
+ assert(validVectorRegister(regNum));
+ v128 result = fVectorRegisters[regNum-UNW_PPC_V0];
+ //fprintf(stderr, "Registers_ppc::getVectorRegister(this=%p, %d) => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n",
+ // this, regNum, result.vec[0], result.vec[1], result.vec[2], result.vec[3]);
+ return result;
+}
+
+void Registers_ppc::setVectorRegister(int regNum, v128 value)
+{
+ assert(validVectorRegister(regNum));
+ //fprintf(stderr, "Registers_ppc::setVectorRegister(this=%p, %d) <0x%08X, 0x%08X, 0x%08X, 0x%08X> => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n",
+ // this, regNum, fVectorRegisters[regNum-UNW_PPC_V0].vec[0], fVectorRegisters[regNum-UNW_PPC_V0].vec[1], fVectorRegisters[regNum-UNW_PPC_V0].vec[2],
+ // fVectorRegisters[regNum-UNW_PPC_V0].vec[3], value.vec[0], value.vec[1], value.vec[2], value.vec[3]);
+ fVectorRegisters[regNum-UNW_PPC_V0] = value;
+}
+
+
+inline const char* Registers_ppc::getRegisterName(int regNum)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return "ip";
+ case UNW_REG_SP:
+ return "sp";
+ case UNW_PPC_R0:
+ return "r0";
+ case UNW_PPC_R1:
+ return "r1";
+ case UNW_PPC_R2:
+ return "r2";
+ case UNW_PPC_R3:
+ return "r3";
+ case UNW_PPC_R4:
+ return "r4";
+ case UNW_PPC_R5:
+ return "r5";
+ case UNW_PPC_R6:
+ return "r6";
+ case UNW_PPC_R7:
+ return "r7";
+ case UNW_PPC_R8:
+ return "r8";
+ case UNW_PPC_R9:
+ return "r9";
+ case UNW_PPC_R10:
+ return "r10";
+ case UNW_PPC_R11:
+ return "r11";
+ case UNW_PPC_R12:
+ return "r12";
+ case UNW_PPC_R13:
+ return "r13";
+ case UNW_PPC_R14:
+ return "r14";
+ case UNW_PPC_R15:
+ return "r15";
+ case UNW_PPC_R16:
+ return "r16";
+ case UNW_PPC_R17:
+ return "r17";
+ case UNW_PPC_R18:
+ return "r18";
+ case UNW_PPC_R19:
+ return "r19";
+ case UNW_PPC_R20:
+ return "r20";
+ case UNW_PPC_R21:
+ return "r21";
+ case UNW_PPC_R22:
+ return "r22";
+ case UNW_PPC_R23:
+ return "r23";
+ case UNW_PPC_R24:
+ return "r24";
+ case UNW_PPC_R25:
+ return "r25";
+ case UNW_PPC_R26:
+ return "r26";
+ case UNW_PPC_R27:
+ return "r27";
+ case UNW_PPC_R28:
+ return "r28";
+ case UNW_PPC_R29:
+ return "r29";
+ case UNW_PPC_R30:
+ return "r30";
+ case UNW_PPC_R31:
+ return "r31";
+ case UNW_PPC_F0:
+ return "fp0";
+ case UNW_PPC_F1:
+ return "fp1";
+ case UNW_PPC_F2:
+ return "fp2";
+ case UNW_PPC_F3:
+ return "fp3";
+ case UNW_PPC_F4:
+ return "fp4";
+ case UNW_PPC_F5:
+ return "fp5";
+ case UNW_PPC_F6:
+ return "fp6";
+ case UNW_PPC_F7:
+ return "fp7";
+ case UNW_PPC_F8:
+ return "fp8";
+ case UNW_PPC_F9:
+ return "fp9";
+ case UNW_PPC_F10:
+ return "fp10";
+ case UNW_PPC_F11:
+ return "fp11";
+ case UNW_PPC_F12:
+ return "fp12";
+ case UNW_PPC_F13:
+ return "fp13";
+ case UNW_PPC_F14:
+ return "fp14";
+ case UNW_PPC_F15:
+ return "fp15";
+ case UNW_PPC_F16:
+ return "fp16";
+ case UNW_PPC_F17:
+ return "fp17";
+ case UNW_PPC_F18:
+ return "fp18";
+ case UNW_PPC_F19:
+ return "fp19";
+ case UNW_PPC_F20:
+ return "fp20";
+ case UNW_PPC_F21:
+ return "fp21";
+ case UNW_PPC_F22:
+ return "fp22";
+ case UNW_PPC_F23:
+ return "fp23";
+ case UNW_PPC_F24:
+ return "fp24";
+ case UNW_PPC_F25:
+ return "fp25";
+ case UNW_PPC_F26:
+ return "fp26";
+ case UNW_PPC_F27:
+ return "fp27";
+ case UNW_PPC_F28:
+ return "fp28";
+ case UNW_PPC_F29:
+ return "fp29";
+ case UNW_PPC_F30:
+ return "fp30";
+ case UNW_PPC_F31:
+ return "fp31";
+ case UNW_PPC_LR:
+ return "lr";
+ default:
+ return "unknown register";
+ }
+
+
+}
+
+
+} // namespace libunwind
+
+
+
+#endif // __REGISTERS_HPP__
+
+
+
+
#include "macho_relocatable_file.h"
#include "lto_file.h"
+// #defines are a work around for <rdar://problem/8760268>
+#define __STDC_LIMIT_MACROS 1
+#define __STDC_CONSTANT_MACROS 1
#include "llvm-c/lto.h"
case CPU_TYPE_X86_64:
return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "x86_64-");
case CPU_TYPE_ARM:
- switch ( subarch ) {
- case CPU_SUBTYPE_ARM_V6:
- return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "armv6-");
- case CPU_SUBTYPE_ARM_V7:
- return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "thumbv7-");
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( subarch == t->subType )
+ return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefix);
}
break;
case CPU_TYPE_POWERPC:
case CPU_TYPE_X86_64:
return "x86_64";
case CPU_TYPE_ARM:
- if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "armv6-") )
- return "armv6";
- if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "thumbv7-") )
- return "armv7";
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, t->llvmTriplePrefix) )
+ return t->subTypeName;
+ }
return "arm";
}
return "unknown bitcode architecture";
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
time_t mTime, uint32_t ordinal, bool linkingFlatNamespace,
bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin,
- bool logAllFiles);
+ ld::MacVersionMin macMin, ld::IOSVersionMin iPhoneMin, bool addVers,
+ bool logAllFiles, const char* installPath, bool indirectDylib);
virtual ~File() {}
// overrides of ld::File
virtual bool deadStrippable() const { return _deadStrippable; }
virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; }
virtual bool hasWeakDefinition(const char* name) const;
+ virtual bool allSymbolsAreWeakImported() const;
protected:
public:
bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
};
- struct AtomAndWeak { ld::Atom* atom; bool weak; bool tlv; pint_t address; };
+ struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; pint_t address; };
typedef __gnu_cxx::hash_map<const char*, AtomAndWeak, __gnu_cxx::hash<const char*>, CStringEquals> NameToAtomMap;
typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet;
static const char* objCInfoSectionName();
const ld::MacVersionMin _macVersionMin;
- const ld::IPhoneVersionMin _iPhoneVersionMin;
+ const ld::IOSVersionMin _iOSVersionMin;
+ const bool _addVersionLoadCommand;
bool _linkingFlat;
bool _implicitlyLinkPublicDylibs;
ld::File::ObjcConstraint _objcContraint;
template <typename A>
File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* pth, time_t mTime, uint32_t ord,
bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs,
- ld::MacVersionMin macMin, ld::IPhoneVersionMin iPhoneMin, bool logAllFiles)
+ ld::MacVersionMin macMin, ld::IOSVersionMin iOSMin, bool addVers,
+ bool logAllFiles, const char* targetInstallPath, bool indirectDylib)
: ld::dylib::File(strdup(pth), mTime, ord),
- _macVersionMin(macMin), _iPhoneVersionMin(iPhoneMin),
+ _macVersionMin(macMin), _iOSVersionMin(iOSMin), _addVersionLoadCommand(addVers),
_linkingFlat(linkingFlatNamespace), _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
_objcContraint(ld::File::objcConstraintNone),
_importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
case LC_SUB_CLIENT:
_allowableClients.push_back(strdup(((macho_sub_client_command<P>*)cmd)->client()));
break;
+ case LC_VERSION_MIN_MACOSX:
+ if ( _addVersionLoadCommand && !indirectDylib && (_iOSVersionMin != ld::iOSVersionUnset) )
+ warning("building for iOS, but linking against dylib built for MacOSX: %s", pth);
+ break;
+ case LC_VERSION_MIN_IPHONEOS:
+ if ( _addVersionLoadCommand && !indirectDylib && (_macVersionMin != ld::macVersionUnset) )
+ warning("building for MacOSX, but linking against dylib built for iOS: %s", pth);
+ break;
case macho_segment_command<P>::CMD:
// check for Objective-C info
if ( strcmp(((macho_segment_command<P>*)cmd)->segname(), objCInfoSegmentName()) == 0 ) {
entry.path = strdup(((macho_dylib_command<P>*)cmd)->name());
entry.dylib = NULL;
entry.reExport = (cmd->cmd() == LC_REEXPORT_DYLIB);
- _dependentDylibs.push_back(entry);
+ if ( (targetInstallPath == NULL) || (strcmp(targetInstallPath, entry.path) != 0) )
+ _dependentDylibs.push_back(entry);
break;
}
cmd = (const macho_load_command<P>*)(((char*)cmd)+cmd->cmdsize());
if ( _macVersionMin != ld::macVersionUnset ) {
sprintf(curOSVers, "$os%d.%d$", (_macVersionMin >> 16), ((_macVersionMin >> 8) & 0xFF));
}
- else if ( _iPhoneVersionMin != ld::iPhoneVersionUnset ) {
- sprintf(curOSVers, "$os%d.%d$", (_iPhoneVersionMin >> 16), ((_iPhoneVersionMin >> 8) & 0xFF));
+ else if ( _iOSVersionMin != ld::iOSVersionUnset ) {
+ sprintf(curOSVers, "$os%d.%d$", (_iOSVersionMin >> 16), ((_iOSVersionMin >> 8) & 0xFF));
}
else {
assert(0 && "targeting neither macosx nor iphoneos");
if ( _ignoreExports.count(name) == 0 ) {
AtomAndWeak bucket;
bucket.atom = NULL;
- bucket.weak = weakDef;
+ bucket.weakDef = weakDef;
bucket.tlv = tlv;
bucket.address = address;
if ( _s_logHashtable ) fprintf(stderr, " adding %s to hash table for %s\n", name, this->path());
typename NameToAtomMap::const_iterator pos = _atoms.find(name);
if ( pos != _atoms.end() ) {
- return pos->second.weak;
+ return pos->second.weakDef;
}
else {
// look in children that I re-export
//fprintf(stderr, "getJustInTimeAtomsFor: %s NOT found in %s, looking in child %s\n", name, this->path(), (*it)->getInstallPath());
typename NameToAtomMap::iterator cpos = it->dylib->_atoms.find(name);
if ( cpos != it->dylib->_atoms.end() )
- return cpos->second.weak;
+ return cpos->second.weakDef;
}
}
}
return false;
}
+
+// <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;
+ //fprintf(stderr, "%s:\n", this->path());
+ for (typename NameToAtomMap::const_iterator it = _atoms.begin(); it != _atoms.end(); ++it) {
+ const ld::Atom* atom = it->second.atom;
+ if ( atom != NULL ) {
+ if ( atom->weakImported() )
+ foundWeakImport = true;
+ else
+ foundNonWeakImport = true;
+ //fprintf(stderr, " weak_import=%d, name=%s\n", atom->weakImported(), it->first);
+ }
+ }
+
+ // 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, pint_t* defAddress) const
{
- // check myself
+ if ( _ignoreExports.count(name) != 0 )
+ return false;
+
+// check myself
typename NameToAtomMap::iterator pos = _atoms.find(name);
if ( pos != _atoms.end() ) {
- *weakDef = pos->second.weak;
+ *weakDef = pos->second.weakDef;
*tlv = pos->second.tlv;
*defAddress = pos->second.address;
return true;
AtomAndWeak bucket;
- if ( this->containsOrReExports(name, &bucket.weak, &bucket.tlv, &bucket.address) ) {
- bucket.atom = new ExportAtom<A>(*this, name, bucket.weak, bucket.tlv, bucket.address);
+ 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;
if ( _s_logHashtable ) fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path());
}
-struct ParserOptions {
- bool linkingFlat;
- bool linkingMain;
- bool addImplictDylibs;
- ld::MacVersionMin macOSMin;
- ld::IPhoneVersionMin iphoneOSMin;
-};
-
-
template <typename A>
class Parser
{
static bool validFile(const uint8_t* fileContent, bool executableOrDyliborBundle);
static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t mTime,
- uint32_t ordinal, const Options& opts) {
+ uint32_t ordinal, const Options& opts, bool indirectDylib) {
return new File<A>(fileContent, fileLength, path, mTime,
ordinal, opts.flatNamespace(),
opts.linkingMainExecutable(),
opts.implicitlyLinkIndirectPublicDylibs(),
opts.macosxVersionMin(),
- opts.iphoneOSVersionMin(),
- opts.logAllFiles());
+ opts.iOSVersionMin(),
+ opts.addVersionLoadCommand(),
+ opts.logAllFiles(),
+ opts.installPath(),
+ indirectDylib);
}
};
// main function used by linker to instantiate ld::Files
//
ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength,
- const char* path, time_t modTime, const Options& opts, uint32_t ordinal, bool bundleLoader)
+ const char* path, time_t modTime, const Options& opts, uint32_t ordinal,
+ bool bundleLoader, bool indirectDylib)
{
switch ( opts.architecture() ) {
case CPU_TYPE_X86_64:
if ( Parser<x86_64>::validFile(fileContent, bundleLoader) )
- return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
case CPU_TYPE_I386:
if ( Parser<x86>::validFile(fileContent, bundleLoader) )
- return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
case CPU_TYPE_ARM:
if ( Parser<arm>::validFile(fileContent, bundleLoader) )
- return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
case CPU_TYPE_POWERPC:
if ( Parser<ppc>::validFile(fileContent, bundleLoader) )
- return Parser<ppc>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ return Parser<ppc>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
case CPU_TYPE_POWERPC64:
if ( Parser<ppc64>::validFile(fileContent, bundleLoader) )
- return Parser<ppc64>::parse(fileContent, fileLength, path, modTime, ordinal, opts);
+ return Parser<ppc64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
break;
}
return NULL;
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
namespace dylib {
extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
- time_t modTime, const Options& opts, uint32_t ordinal, bool bundleLoader);
+ time_t modTime, const Options& opts, uint32_t ordinal,
+ bool bundleLoader, bool indirectDylib);
} // namespace dylib
} // namespace mach_o
*
* @APPLE_LICENSE_HEADER_END@
*/
-
+
#include <stdint.h>
#include <stdlib.h>
#include "MachOFileAbstraction.hpp"
-#include <libunwind/DwarfInstructions.hpp>
-#include <libunwind/AddressSpace.hpp>
-#include <libunwind/Registers.hpp>
+#include "libunwind/DwarfInstructions.hpp"
+#include "libunwind/AddressSpace.hpp"
+#include "libunwind/Registers.hpp"
#include <vector>
#include <set>
template <typename A> class Atom;
template <typename A> class Section;
template <typename A> class CFISection;
+template <typename A> class CUSection;
template <typename A>
class File : public ld::relocatable::File
virtual bool addFollowOnFixups() const { return ! _file.canScatterAtoms(); }
virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&) = 0;
+ const struct Parser<A>::CFI_CU_InfoArrays&) = 0;
virtual uint32_t computeAtomCount(class Parser<A>& parser,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&) = 0;
- virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&);
+ const struct Parser<A>::CFI_CU_InfoArrays&) = 0;
+ virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&);
virtual bool addRelocFixup(class Parser<A>& parser, const macho_relocation_info<P>*);
virtual unsigned long contentHash(const class Atom<A>* atom, const ld::IndirectBindingTable& ind) const { return 0; }
virtual bool canCoalesceWith(const class Atom<A>* atom, const ld::Atom& rhs,
const ld::IndirectBindingTable& ind) const { return false; }
+ static const char* makeSectionName(const macho_section<typename A::P>* s);
protected:
Section(File<A>& f, const macho_section<typename A::P>* s)
Atom<A>* findContentAtomByAddress(pint_t addr, class Atom<A>* start, class Atom<A>* end);
uint32_t x86_64PcRelOffset(uint8_t r_type);
static const char* makeSegmentName(const macho_section<typename A::P>* s);
- static const char* makeSectionName(const macho_section<typename A::P>* s);
static bool readable(const macho_section<typename A::P>* s);
static bool writable(const macho_section<typename A::P>* s);
static bool exectuable(const macho_section<typename A::P>* s);
uint32_t cfiCount();
virtual ld::Atom::ContentType contentType() { return ld::Atom::typeCFI; }
- virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&);
- virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&);
- virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&);
+ virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFI_CU_InfoArrays&);
+ virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFI_CU_InfoArrays&);
+ virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&);
virtual bool addFollowOnFixups() const { return false; }
};
+template <typename A>
+class CUSection : public Section<A>
+{
+public:
+ CUSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s)
+ : Section<A>(f, s) { }
+
+ typedef typename A::P::uint_t pint_t;
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+
+ virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFI_CU_InfoArrays&) { return 0; }
+ virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFI_CU_InfoArrays&) { return 0; }
+ virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&);
+ virtual bool addFollowOnFixups() const { return false; }
+
+ struct Info {
+ pint_t functionStartAddress;
+ uint32_t functionSymbolIndex;
+ uint32_t rangeLength;
+ uint32_t compactUnwindInfo;
+ const char* personality;
+ pint_t lsdaAddress;
+ Atom<A>* function;
+ Atom<A>* lsda;
+ };
+
+ uint32_t count();
+ void parse(class Parser<A>& parser, uint32_t cnt, Info array[]);
+
+
+private:
+
+ const char* personalityName(class Parser<A>& parser, const macho_relocation_info<P>* reloc);
+
+ static int infoSorter(const void* l, const void* r);
+
+};
+
+
template <typename A>
class TentativeDefinitionSection : public Section<A>
{
virtual bool addFollowOnFixups() const { return false; }
virtual Atom<A>* findAtomByAddress(typename A::P::uint_t addr) { throw "TentativeDefinitionSection::findAtomByAddress() should never be called"; }
virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&);
+ const struct Parser<A>::CFI_CU_InfoArrays&);
virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&);
- virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&) {}
+ const struct Parser<A>::CFI_CU_InfoArrays&);
+ virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&) {}
private:
typedef typename A::P::uint_t pint_t;
typedef typename A::P P;
virtual bool addFollowOnFixups() const { return false; }
virtual Atom<A>* findAtomByAddress(typename A::P::uint_t addr) { throw "AbsoluteSymbolSection::findAtomByAddress() should never be called"; }
virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&);
+ const struct Parser<A>::CFI_CU_InfoArrays&);
virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&);
- virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&) {}
+ const struct Parser<A>::CFI_CU_InfoArrays&);
+ virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&) {}
virtual Atom<A>* findAbsAtomForValue(typename A::P::uint_t);
private:
virtual ld::Atom::ContentType contentType() { return _type; }
virtual bool dontDeadStrip();
virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&);
+ const struct Parser<A>::CFI_CU_InfoArrays&);
virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&);
+ const struct Parser<A>::CFI_CU_InfoArrays&);
protected:
typedef typename A::P::uint_t pint_t;
typedef typename A::P P;
public:
ImplicitSizeSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s)
: Section<A>(f, s) { }
- virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&);
- virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFIInfoArray&);
+ virtual uint32_t computeAtomCount(class Parser<A>& parser, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFI_CU_InfoArrays&);
+ virtual uint32_t appendAtoms(class Parser<A>& parser, uint8_t* buffer, struct Parser<A>::LabelAndCFIBreakIterator& it, const struct Parser<A>::CFI_CU_InfoArrays&);
protected:
typedef typename A::P::uint_t pint_t;
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
typedef typename A::P P;
- virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&);
+ virtual void makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&);
virtual ld::Atom::ContentType contentType() { return ld::Atom::typeNonLazyPointer; }
virtual ld::Atom::Alignment alignmentForAddress(pint_t addr) { return ld::Atom::Alignment(log2(sizeof(pint_t))); }
virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "non_lazy_ptr"; }
virtual Atom<A>* findAtomByAddress(pint_t addr);
virtual const char* unlabeledAtomName(Parser<A>&, pint_t) { return "cstring"; }
virtual pint_t elementSizeAtAddress(pint_t addr);
+ virtual bool ignoreLabel(const char* label);
virtual bool useElementAt(Parser<A>& parser,
struct Parser<A>::LabelAndCFIBreakIterator& it, pint_t addr);
virtual ld::Atom::Combine combine(Parser<A>&, pint_t) { return ld::Atom::combineByNameAndContent; }
File<A>& machofile() const { return ((Section<A>*)(this->_section))->file(); }
void setFixupsRange(uint32_t s, uint32_t c);
void setUnwindInfoRange(uint32_t s, uint32_t c);
+ void extendUnwindInfoRange();
void setLineInfoRange(uint32_t s, uint32_t c);
bool roomForMoreLineInfoCount() { return (_lineInfoCount < ((1<<kLineInfoCountBits)-1)); }
void incrementLineInfoCount() { assert(roomForMoreLineInfoCount()); ++_lineInfoCount; }
_unwindInfoCount = count;
}
+template <typename A>
+void Atom<A>::extendUnwindInfoRange()
+{
+ if ( _unwindInfoCount+1 >= (1 << kUnwindInfoCountBits) )
+ throwf("too many compact unwind infos in function %s", this->name());
+ _unwindInfoCount += 1;
+}
+
template <typename A>
void Atom<A>::setLineInfoRange(uint32_t startIndex, uint32_t count)
{
void Atom<arm>::verifyAlignment() const
{
if ( (this->section().type() == ld::Section::typeCode) && ! isThumb() ) {
- if ( (_objAddress % 4) != 0 )
- warning("ARM function %s not 4-byte aligned", this->name());
+ if ( ((_objAddress % 4) != 0) || (this->alignment().powerOf2 < 2) )
+ warning("ARM function not 4-byte aligned: %s from %s", this->name(), this->file()->path());
}
}
uint32_t symIndex;
};
- struct CFIInfoArray {
+ struct CFI_CU_InfoArrays {
typedef typename CFISection<A>::CFI_Atom_Info CFI_Atom_Info;
- CFIInfoArray(const CFI_Atom_Info* cfia, uint32_t cfiac) : array(cfia), count(cfiac) {}
- const CFI_Atom_Info* const array;
- const uint32_t count;
+ typedef typename CUSection<A>::Info CU_Info;
+ CFI_CU_InfoArrays(const CFI_Atom_Info* cfiAr, uint32_t cfiC, CU_Info* cuAr, uint32_t cuC)
+ : cfiArray(cfiAr), cuArray(cuAr), cfiCount(cfiC), cuCount(cuC) {}
+ const CFI_Atom_Info* const cfiArray;
+ CU_Info* const cuArray;
+ const uint32_t cfiCount;
+ const uint32_t cuCount;
};
+
private:
friend class Section<A>;
sectionTypeNonLazy, sectionTypeCFI, sectionTypeCString, sectionTypeCStringPointer,
sectionTypeUTF16Strings, sectionTypeCFString, sectionTypeObjC2ClassRefs, typeObjC2CategoryList,
sectionTypeObjC1Classes, sectionTypeSymboled, sectionTypeObjC1ClassRefs,
- sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs };
+ sectionTypeTentativeDefinitions, sectionTypeAbsoluteSymbols, sectionTypeTLVDefs,
+ sectionTypeCompactUnwind };
template <typename P>
struct MachOSectionAndSectionClass
return 1;
}
};
+
+ struct ParserAndSectionsArray { Parser* parser; const uint32_t* sortedSectionsArray; };
+
Parser(const uint8_t* fileContent, uint64_t fileLength,
const char* path, time_t modTime,
uint8_t loadCommandSizeMask();
bool parseLoadCommands();
void makeSections();
- void checkForLSDA();
void prescanSymbolTable();
- void makeSortedSymbolsArray(uint32_t array[]);
+ void makeSortedSymbolsArray(uint32_t symArray[], const uint32_t sectionArray[]);
+ void makeSortedSectionsArray(uint32_t array[]);
static int pointerSorter(const void* l, const void* r);
static int symbolIndexSorter(void* extra, const void* l, const void* r);
+ static int sectionIndexSorter(void* extra, const void* l, const void* r);
+
void parseDebugInfo();
void parseStabs();
static bool isConstFunStabs(const char *stabStr);
// filled in by parse()
CFISection<A>* _EHFrameSection;
+ CUSection<A>* _compactUnwindSection;
AbsoluteSymbolSection<A>* _absoluteSection;
- uint32_t _lsdaTextSectionNum;
- uint32_t _lsdaDataSectionNum;
uint32_t _tentativeDefinitionCount;
uint32_t _absoluteSymbolCount;
uint32_t _symbolsInSections;
_indirectTable(NULL), _indirectTableCount(0),
_undefinedStartIndex(0), _undefinedEndIndex(0),
_sectionsStart(NULL), _machOSectionsCount(0), _hasUUID(false),
- _EHFrameSection(NULL), _absoluteSection(NULL),
- _lsdaTextSectionNum(0), _lsdaDataSectionNum(0),
+ _EHFrameSection(NULL), _compactUnwindSection(NULL), _absoluteSection(NULL),
_tentativeDefinitionCount(0), _absoluteSymbolCount(0),
_symbolsInSections(0), _hasLongBranchStubs(false), _AppleObjc(false),
_overlappingSymbols(false), _convertUnwindInfo(convertDUI),
return NULL;
if ( header->cputype() != CPU_TYPE_ARM )
return NULL;
- switch ( header->cpusubtype() ) {
- case CPU_SUBTYPE_ARM_V4T:
- return "armv4t";
- case CPU_SUBTYPE_ARM_V5TEJ:
- return "armv5";
- case CPU_SUBTYPE_ARM_V6:
- return "armv6";
- case CPU_SUBTYPE_ARM_V7:
- return "armv7";
- case CPU_SUBTYPE_ARM_ALL:
- return "arm-ALL";
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( t->subType == (cpu_subtype_t)header->cpusubtype() ) {
+ return t->subTypeName;
+ }
}
return "arm???";
}
while ( symIndex < sortedSymbolCount ) {
const macho_nlist<P>& sym = parser.symbolFromIndex(sortedSymbolIndexes[symIndex]);
pint_t nextSymbolAddr = sym.n_value();
- if ( (nextSymbolAddr >= startAddr) && (sym.n_sect() >= sectNum) )
+ //fprintf(stderr, "sectNum=%d, nextSymbolAddr=0x%08llX, name=%s\n", sectNum, (uint64_t)nextSymbolAddr, parser.nameFromSymbol(sym));
+ if ( (nextSymbolAddr > startAddr) || ((nextSymbolAddr == startAddr) && (sym.n_sect() == sectNum)) )
break;
++symIndex;
}
if ( ! parseLoadCommands() )
return _file;
+ // make array of
+ uint32_t sortedSectionIndexes[_machOSectionsCount];
+ this->makeSortedSectionsArray(sortedSectionIndexes);
+
// make symbol table sorted by address
- this->checkForLSDA();
this->prescanSymbolTable();
uint32_t sortedSymbolIndexes[_symbolsInSections];
- this->makeSortedSymbolsArray(sortedSymbolIndexes);
+ this->makeSortedSymbolsArray(sortedSymbolIndexes, sortedSectionIndexes);
// allocate Section<A> object for each mach-o section
makeSections();
- // if it exists, do special parsing of __eh_frame section
+ // if it exists, do special early parsing of __compact_unwind section
+ uint32_t countOfCUs = 0;
+ if ( _compactUnwindSection != NULL )
+ countOfCUs = _compactUnwindSection->count();
+ uint8_t cuInfoBuffer[sizeof(typename CUSection<A>::Info) * countOfCUs];
+ typename CUSection<A>::Info* cuInfoArray = (typename CUSection<A>::Info*)cuInfoBuffer;
+ if ( countOfCUs != 0 )
+ _compactUnwindSection->parse(*this, countOfCUs, cuInfoArray);
+
+ // if it exists, do special early parsing of __eh_frame section
// stack allocate array of CFI_Atom_Info
uint32_t countOfCFIs = 0;
if ( _EHFrameSection != NULL )
++cfiStartsCount;
}
}
- CFIInfoArray cfis(cfiArray, countOfCFIs);
+ CFI_CU_InfoArrays cfis(cfiArray, countOfCFIs, cuInfoArray, countOfCUs);
// create sorted array of function starts and lsda starts
pint_t cfiStartsArray[cfiStartsCount];
uint8_t* atoms = _file->_atomsArray + _file->_atomsArrayCount*sizeof(Atom<A>);
breakIterator2.beginSection();
uint32_t count = sections[i]->appendAtoms(*this, atoms, breakIterator2, cfis);
- //fprintf(stderr, "append count=%u for section %s\n", count, sections[i]->machoSection()->sectname());
+ //fprintf(stderr, "append count=%u for section %s/%s\n", count, sections[i]->machoSection()->segname(), sections[i]->machoSection()->sectname());
_file->_atomsArrayCount += count;
}
assert( _file->_atomsArrayCount == computedAtomCount && "more atoms allocated than expected");
_allFixups.clear();
// add unwind info
- _file->_unwindInfos.reserve(countOfFDEs);
+ _file->_unwindInfos.reserve(countOfFDEs+countOfCUs);
for(uint32_t i=0; i < countOfCFIs; ++i) {
if ( cfiArray[i].isCIE )
continue;
func->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1);
}
}
-
+ // apply compact infos in __LD,__compact_unwind section to each function
+ // if function also has dwarf unwind, CU will override it
+ Atom<A>* lastFunc = NULL;
+ uint32_t lastEnd = 0;
+ for(uint32_t i=0; i < countOfCUs; ++i) {
+ typename CUSection<A>::Info* info = &cuInfoArray[i];
+ assert(info->function != NULL);
+ ld::Atom::UnwindInfo ui;
+ ui.startOffset = info->functionStartAddress - info->function->objectAddress();
+ ui.unwindInfo = info->compactUnwindInfo;
+ _file->_unwindInfos.push_back(ui);
+ // if previous is for same function, extend range
+ if ( info->function == lastFunc ) {
+ if ( lastEnd != ui.startOffset ) {
+ if ( lastEnd < ui.startOffset )
+ warning("__LD,__compact_unwind entries for %s have a gap at offset 0x%0X", info->function->name(), lastEnd);
+ else
+ warning("__LD,__compact_unwind entries for %s overlap at offset 0x%0X", info->function->name(), lastEnd);
+ }
+ lastFunc->extendUnwindInfoRange();
+ }
+ else
+ info->function->setUnwindInfoRange(_file->_unwindInfos.size()-1, 1);
+ lastFunc = info->function;
+ lastEnd = ui.startOffset + info->rangeLength;
+ }
+
// parse dwarf debug info to get line info
this->parseDebugInfo();
return true;
}
-template <>
-void Parser<arm>::checkForLSDA()
-{
- // ARM has no FDEs, so need labels to break up section into atoms
-}
-
-template <typename A>
-void Parser<A>::checkForLSDA()
-{
- // ignore labels on __gcc_except_tab section, we'll break it into atoms based on FDE info
- for (uint32_t i=0; i < _machOSectionsCount; ++i) {
- const macho_section<P>* sect = &_sectionsStart[i];
- if ( strncmp(sect->sectname(), "__gcc_except_tab", 16) == 0 ) {
- if ( strcmp(sect->segname(), "__TEXT") == 0 ) {
- assert(_lsdaTextSectionNum == 0);
- _lsdaTextSectionNum = i+1;
- }
- else if ( strcmp(sect->segname(), "__DATA") == 0 ) {
- assert(_lsdaDataSectionNum == 0);
- _lsdaDataSectionNum = i+1;
- }
- }
- }
-}
-
template <typename A>
void Parser<A>::prescanSymbolTable()
if ( symbolName[0] == 'L' )
continue;
- // ignore labels in __gcc_except_tab section
- if ( (_lsdaTextSectionNum != 0) && (sym.n_sect() == _lsdaTextSectionNum) )
- continue;
- if ( (_lsdaDataSectionNum != 0) && (sym.n_sect() == _lsdaDataSectionNum) )
- continue;
-
// how many def syms in each section
if ( sym.n_sect() > _machOSectionsCount )
throw "bad n_sect in symbol table";
}
template <typename A>
-int Parser<A>::symbolIndexSorter(void* extra, const void* l, const void* r)
+int Parser<A>::sectionIndexSorter(void* extra, const void* l, const void* r)
{
Parser<A>* parser = (Parser<A>*)extra;
const uint32_t* left = (uint32_t*)l;
const uint32_t* right = (uint32_t*)r;
+ const macho_section<P>* leftSect = parser->machOSectionFromSectionIndex(*left);
+ const macho_section<P>* rightSect = parser->machOSectionFromSectionIndex(*right);
+
+ // can't just return difference because 64-bit diff does not fit in 32-bit return type
+ int64_t result = leftSect->addr() - rightSect->addr();
+ if ( result == 0 ) {
+ // two sections with same start address
+ // one with zero size goes first
+ bool leftEmpty = ( leftSect->size() == 0 );
+ bool rightEmpty = ( rightSect->size() == 0 );
+ if ( leftEmpty != rightEmpty ) {
+ return ( rightEmpty ? 1 : -1 );
+ }
+ if ( !leftEmpty && !rightEmpty )
+ throwf("overlapping sections");
+ // both empty, so chose file order
+ return ( rightSect - leftSect );
+ }
+ else if ( result < 0 )
+ return -1;
+ else
+ return 1;
+}
+
+template <typename A>
+void Parser<A>::makeSortedSectionsArray(uint32_t array[])
+{
+ const bool log = false;
+
+ if ( log ) {
+ fprintf(stderr, "unsorted sections:\n");
+ for(unsigned int i=0; i < _machOSectionsCount; ++i )
+ fprintf(stderr, "0x%08llX %s %s\n", _sectionsStart[i].addr(), _sectionsStart[i].segname(), _sectionsStart[i].sectname());
+ }
+
+ // sort by symbol table address
+ for (uint32_t i=0; i < _machOSectionsCount; ++i)
+ array[i] = i;
+ ::qsort_r(array, _machOSectionsCount, sizeof(uint32_t), this, §ionIndexSorter);
+
+ if ( log ) {
+ fprintf(stderr, "sorted sections:\n");
+ for(unsigned int i=0; i < _machOSectionsCount; ++i )
+ fprintf(stderr, "0x%08llX %s %s\n", _sectionsStart[array[i]].addr(), _sectionsStart[array[i]].segname(), _sectionsStart[array[i]].sectname());
+ }
+}
+
+
+
+template <typename A>
+int Parser<A>::symbolIndexSorter(void* extra, const void* l, const void* r)
+{
+ ParserAndSectionsArray* extraInfo = (ParserAndSectionsArray*)extra;
+ Parser<A>* parser = extraInfo->parser;
+ const uint32_t* sortedSectionsArray = extraInfo->sortedSectionsArray;
+ const uint32_t* left = (uint32_t*)l;
+ const uint32_t* right = (uint32_t*)r;
const macho_nlist<P>& leftSym = parser->symbolFromIndex(*left);
const macho_nlist<P>& rightSym = parser->symbolFromIndex(*right);
// can't just return difference because 64-bit diff does not fit in 32-bit return type
if ( result == 0 ) {
// two symbols with same address
// if in different sections, sort earlier section first
- if ( leftSym.n_sect() != rightSym.n_sect() )
- return (leftSym.n_sect() - rightSym.n_sect());
- //, means one is an alias
+ if ( leftSym.n_sect() != rightSym.n_sect() ) {
+ for (uint32_t i=0; i < parser->machOSectionCount(); ++i) {
+ if ( sortedSectionsArray[i]+1 == leftSym.n_sect() )
+ return -1;
+ if ( sortedSectionsArray[i]+1 == rightSym.n_sect() )
+ return 1;
+ }
+ }
+ // two symbols in same section, means one is an alias
// if only one is global, make the other an alias (sort first)
if ( (leftSym.n_type() & N_EXT) != (rightSym.n_type() & N_EXT) ) {
if ( (rightSym.n_type() & N_EXT) != 0 )
return 1;
}
+
template <typename A>
-void Parser<A>::makeSortedSymbolsArray(uint32_t array[])
+void Parser<A>::makeSortedSymbolsArray(uint32_t array[], const uint32_t sectionArray[])
{
+ const bool log = false;
+
uint32_t* p = array;
for (uint32_t i=0; i < this->_symbolCount; ++i) {
const macho_nlist<P>& sym = symbolFromIndex(i);
if ( symbolName[0] == 'L' )
continue;
- // ignore labels in __gcc_except_tab section
- if ( (_lsdaTextSectionNum != 0) && (sym.n_sect() == _lsdaTextSectionNum) )
- continue;
- if ( (_lsdaDataSectionNum != 0) && (sym.n_sect() == _lsdaDataSectionNum) )
- continue;
-
// how many def syms in each section
if ( sym.n_sect() > _machOSectionsCount )
throw "bad n_sect in symbol table";
assert(p == &array[_symbolsInSections] && "second pass over symbol table yield a different number of symbols");
// sort by symbol table address
- ::qsort_r(array, _symbolsInSections, sizeof(uint32_t), this, &symbolIndexSorter);
+ ParserAndSectionsArray extra = { this, sectionArray };
+ ::qsort_r(array, _symbolsInSections, sizeof(uint32_t), &extra, &symbolIndexSorter);
// look for two symbols at same address
_overlappingSymbols = false;
}
}
- //fprintf(stderr, "sorted symbols:\n");
- //for(unsigned int i=0; i < _symbolsInSections; ++i )
- // fprintf(stderr, "0x%09llX symIndex=%3d sectNum=%2d, %s\n", symbolFromIndex(array[i]).n_value(), array[i], symbolFromIndex(array[i]).n_sect(), nameFromSymbol(symbolFromIndex(array[i])) );
+ if ( log ) {
+ fprintf(stderr, "sorted symbols:\n");
+ for(unsigned int i=0; i < _symbolsInSections; ++i )
+ fprintf(stderr, "0x%09llX symIndex=%d sectNum=%2d, %s\n", symbolFromIndex(array[i]).n_value(), array[i], symbolFromIndex(array[i]).n_sect(), nameFromSymbol(symbolFromIndex(array[i])) );
+ }
}
unsigned int count = 0;
for (uint32_t i=0; i < _machOSectionsCount; ++i) {
const macho_section<P>* sect = &_sectionsStart[i];
- // ignore dwarf sections
if ( (sect->flags() & S_ATTR_DEBUG) != 0 ) {
- // note that .o file has dwarf
- _file->_debugInfoKind = ld::relocatable::File::kDebugInfoDwarf;
- // save off iteresting dwarf sections
- if ( strcmp(sect->sectname(), "__debug_info") == 0 )
- _file->_dwarfDebugInfoSect = sect;
- else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 )
- _file->_dwarfDebugAbbrevSect = sect;
- else if ( strcmp(sect->sectname(), "__debug_line") == 0 )
- _file->_dwarfDebugLineSect = sect;
- else if ( strcmp(sect->sectname(), "__debug_str") == 0 )
- _file->_dwarfDebugStringSect = sect;
- // linker does not propagate dwarf sections to output file
- continue;
+ if ( strcmp(sect->segname(), "__DWARF") == 0 ) {
+ // note that .o file has dwarf
+ _file->_debugInfoKind = ld::relocatable::File::kDebugInfoDwarf;
+ // save off iteresting dwarf sections
+ if ( strcmp(sect->sectname(), "__debug_info") == 0 )
+ _file->_dwarfDebugInfoSect = sect;
+ else if ( strcmp(sect->sectname(), "__debug_abbrev") == 0 )
+ _file->_dwarfDebugAbbrevSect = sect;
+ else if ( strcmp(sect->sectname(), "__debug_line") == 0 )
+ _file->_dwarfDebugLineSect = sect;
+ else if ( strcmp(sect->sectname(), "__debug_str") == 0 )
+ _file->_dwarfDebugStringSect = sect;
+ // linker does not propagate dwarf sections to output file
+ continue;
+ }
+ else if ( strcmp(sect->segname(), "__LD") == 0 ) {
+ if ( strncmp(sect->sectname(), "__compact_unwind", 16) == 0 ) {
+ machOSects[count].sect = sect;
+ totalSectionsSize += sizeof(CUSection<A>);
+ machOSects[count++].type = sectionTypeCompactUnwind;
+ continue;
+ }
+ }
}
// ignore empty __OBJC sections
if ( (sect->size() == 0) && (strcmp(sect->segname(), "__OBJC") == 0) )
_file->_ojcReplacmentClass = true;
if ( sect->size() > 8 ) {
warning("section %s/%s has unexpectedly large size %llu in %s",
- sect->segname(), sect->sectname(), sect->size(), _file->path());
+ sect->segname(), Section<A>::makeSectionName(sect), sect->size(), _file->path());
}
}
else {
- warning("can't parse %s/%s section in %s", sect->segname(), sect->sectname(), _file->path());
+ warning("can't parse %s/%s section in %s", sect->segname(), Section<A>::makeSectionName(sect), _file->path());
}
continue;
}
*objects++ = new (space) TLVDefsSection<A>(*this, *_file, machOSects[i].sect);
space += sizeof(TLVDefsSection<A>);
break;
+ case sectionTypeCompactUnwind:
+ _compactUnwindSection = new (space) CUSection<A>(*this, *_file, machOSects[i].sect);
+ *objects++ = _compactUnwindSection;
+ space += sizeof(CUSection<A>);
+ break;
case sectionTypeTentativeDefinitions:
*objects++ = new (space) TentativeDefinitionSection<A>(*this, *_file);
space += sizeof(TentativeDefinitionSection<A>);
template <typename A>
uint32_t TentativeDefinitionSection<A>::computeAtomCount(class Parser<A>& parser,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
return parser.tentativeDefinitionCount();
}
template <typename A>
uint32_t TentativeDefinitionSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
this->_beginAtoms = (Atom<A>*)p;
uint32_t count = 0;
++alignP2;
}
// limit alignment of extremely large commons to 2^15 bytes (8-page)
- if ( alignP2 > 12 )
- alignP2 = 12;
+ if ( alignP2 > 15 )
+ alignP2 = 15;
Atom<A>* allocatedSpace = (Atom<A>*)p;
new (allocatedSpace) Atom<A>(*this, parser.nameFromSymbol(sym), (pint_t)ULLONG_MAX, size,
ld::Atom::definitionTentative, ld::Atom::combineByName,
template <typename A>
uint32_t AbsoluteSymbolSection<A>::computeAtomCount(class Parser<A>& parser,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
return parser.absoluteSymbolCount();
}
template <typename A>
uint32_t AbsoluteSymbolSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
this->_beginAtoms = (Atom<A>*)p;
uint32_t count = 0;
template <typename A>
uint32_t CFISection<A>::computeAtomCount(class Parser<A>& parser,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray& cfis)
+ const struct Parser<A>::CFI_CU_InfoArrays& cfis)
{
- return cfis.count;
+ return cfis.cfiCount;
}
template <typename A>
uint32_t CFISection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray& cfis)
+ const struct Parser<A>::CFI_CU_InfoArrays& cfis)
{
this->_beginAtoms = (Atom<A>*)p;
// walk CFI_Atom_Info array and create atom for each entry
- const CFI_Atom_Info* start = &cfis.array[0];
- const CFI_Atom_Info* end = &cfis.array[cfis.count];
+ const CFI_Atom_Info* start = &cfis.cfiArray[0];
+ const CFI_Atom_Info* end = &cfis.cfiArray[cfis.cfiCount];
for(const CFI_Atom_Info* a=start; a < end; ++a) {
Atom<A>* space = (Atom<A>*)p;
new (space) Atom<A>(*this, (a->isCIE ? "CIE" : "FDE"), a->address, a->size,
p += sizeof(Atom<A>);
}
this->_endAtoms = (Atom<A>*)p;
- return cfis.count;
+ return cfis.cfiCount;
}
}
template <typename A>
-void CFISection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray& cfis)
+void CFISection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays& cfis)
{
ld::Fixup::Kind store32 = bigEndian() ? ld::Fixup::kindStoreBigEndian32 : ld::Fixup::kindStoreLittleEndian32;
ld::Fixup::Kind store64 = bigEndian() ? ld::Fixup::kindStoreBigEndian64 : ld::Fixup::kindStoreLittleEndian64;
// add all references for FDEs, including implicit group references
- const CFI_Atom_Info* end = &cfis.array[cfis.count];
- for(const CFI_Atom_Info* p = &cfis.array[0]; p < end; ++p) {
+ const CFI_Atom_Info* end = &cfis.cfiArray[cfis.cfiCount];
+ for(const CFI_Atom_Info* p = &cfis.cfiArray[0]; p < end; ++p) {
if ( p->isCIE ) {
// add reference to personality function if used
if ( p->u.cieInfo.personality.targetAddress != CFI_INVALID_ADDRESS ) {
return result;
}
+template <>
+const char* CUSection<x86_64>::personalityName(class Parser<x86_64>& parser, const macho_relocation_info<x86_64::P>* reloc)
+{
+ assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
+ assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
+ const macho_nlist<P>& sym = parser.symbolFromIndex(reloc->r_symbolnum());
+ return parser.nameFromSymbol(sym);
+}
+
+template <>
+const char* CUSection<x86>::personalityName(class Parser<x86>& parser, const macho_relocation_info<x86::P>* reloc)
+{
+ assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
+ assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
+ const macho_nlist<P>& sym = parser.symbolFromIndex(reloc->r_symbolnum());
+ return parser.nameFromSymbol(sym);
+}
+
+template <typename A>
+const char* CUSection<A>::personalityName(class Parser<A>& parser, const macho_relocation_info<P>* reloc)
+{
+ return NULL;
+}
+
+
+template <typename A>
+int CUSection<A>::infoSorter(const void* l, const void* r)
+{
+ // sort references by symbol index, then address
+ const Info* left = (Info*)l;
+ const Info* right = (Info*)r;
+ if ( left->functionSymbolIndex == right->functionSymbolIndex )
+ return (left->functionStartAddress - right->functionStartAddress);
+ else
+ return (left->functionSymbolIndex - right->functionSymbolIndex);
+}
+
+template <typename A>
+void CUSection<A>::parse(class Parser<A>& parser, uint32_t cnt, Info array[])
+{
+ // walk section content and copy to Info array
+ const macho_compact_unwind_entry<P>* const entries = (macho_compact_unwind_entry<P>*)(this->file().fileContent() + this->_machOSection->offset());
+ for (uint32_t i=0; i < cnt; ++i) {
+ Info* info = &array[i];
+ const macho_compact_unwind_entry<P>* entry = &entries[i];
+ info->functionStartAddress = entry->codeStart();
+ info->functionSymbolIndex = 0xFFFFFFFF;
+ info->rangeLength = entry->codeLen();
+ info->compactUnwindInfo = entry->compactUnwindInfo();
+ info->personality = NULL;
+ info->lsdaAddress = entry->lsda();
+ info->function = NULL;
+ info->lsda = NULL;
+ if ( (info->compactUnwindInfo & UNWIND_PERSONALITY_MASK) != 0 )
+ warning("no bits should be set in UNWIND_PERSONALITY_MASK of compact unwind encoding in __LD,__compact_unwind section");
+ if ( info->lsdaAddress != 0 ) {
+ info->compactUnwindInfo |= UNWIND_HAS_LSDA;
+ }
+ }
+
+ // scan relocs, local relocs are useless - ignore them
+ // extern relocs are needed for personality references (possibly for function/lsda refs??)
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(this->file().fileContent() + this->_machOSection->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[this->_machOSection->nreloc()];
+ for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+ if ( reloc->r_extern() ) {
+ // only expect external relocs on some colummns
+ if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry<P>)) == macho_compact_unwind_entry<P>::personalityFieldOffset() ) {
+ uint32_t entryIndex = reloc->r_address() / sizeof(macho_compact_unwind_entry<P>);
+ array[entryIndex].personality = this->personalityName(parser, reloc);
+ }
+ else if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry<P>)) == macho_compact_unwind_entry<P>::lsdaFieldOffset() ) {
+ uint32_t entryIndex = reloc->r_address() / sizeof(macho_compact_unwind_entry<P>);
+ const macho_nlist<P>& lsdaSym = parser.symbolFromIndex(reloc->r_symbolnum());
+ if ( (lsdaSym.n_type() & N_TYPE) == N_SECT )
+ array[entryIndex].lsdaAddress = lsdaSym.n_value();
+ else
+ warning("unexpected extern relocation to lsda in __compact_unwind section");
+ }
+ else if ( (reloc->r_address() % sizeof(macho_compact_unwind_entry<P>)) == macho_compact_unwind_entry<P>::codeStartFieldOffset() ) {
+ uint32_t entryIndex = reloc->r_address() / sizeof(macho_compact_unwind_entry<P>);
+ array[entryIndex].functionSymbolIndex = reloc->r_symbolnum();
+ }
+ else {
+ warning("unexpected extern relocation in __compact_unwind section");
+ }
+ }
+ }
+
+ // sort array by function start address so unwind infos will be contiguous for a given function
+ ::qsort(array, cnt, sizeof(Info), infoSorter);
+}
+
+template <typename A>
+uint32_t CUSection<A>::count()
+{
+ const macho_section<P>* machoSect = this->machoSection();
+ if ( (machoSect->size() % sizeof(macho_compact_unwind_entry<P>)) != 0 )
+ throw "malformed __LD,__compact_unwind section, bad length";
+
+ return machoSect->size() / sizeof(macho_compact_unwind_entry<P>);
+}
+
+template <typename A>
+void CUSection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays& cus)
+{
+ Info* const arrayStart = cus.cuArray;
+ Info* const arrayEnd = &cus.cuArray[cus.cuCount];
+ for (Info* info=arrayStart; info < arrayEnd; ++info) {
+ // if external reloc was used, real address is symbol n_value + addend
+ if ( info->functionSymbolIndex != 0xFFFFFFFF )
+ info->functionStartAddress += parser.symbolFromIndex(info->functionSymbolIndex).n_value();
+ // find function atom from address
+ info->function = parser.findAtomByAddress(info->functionStartAddress);
+ // find lsda atom from address
+ if ( info->lsdaAddress != 0 ) {
+ info->lsda = parser.findAtomByAddress(info->lsdaAddress);
+ // add lsda subordinate
+ typename Parser<A>::SourceLocation src(info->function, info->functionStartAddress - info->function->objectAddress());
+ parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinateLSDA, info->lsda);
+ }
+ if ( info->personality != NULL ) {
+ // add personality subordinate
+ typename Parser<A>::SourceLocation src(info->function, info->functionStartAddress - info->function->objectAddress());
+ parser.addFixup(src, ld::Fixup::k1of1, ld::Fixup::kindNoneGroupSubordinatePersonality, false, info->personality);
+ }
+ }
+
+}
+
template <typename A>
SymboledSection<A>::SymboledSection(Parser<A>& parser, File<A>& f, const macho_section<typename A::P>* s)
: Section<A>(f, s), _type(ld::Atom::typeUnclassified)
template <typename A>
uint32_t SymboledSection<A>::computeAtomCount(class Parser<A>& parser,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
const pint_t startAddr = this->_machOSection->addr();
const pint_t endAddr = startAddr + this->_machOSection->size();
template <typename A>
uint32_t SymboledSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
this->_beginAtoms = (Atom<A>*)p;
this->_hasAliases = true;
}
else {
+ ld::Atom::SymbolTableInclusion inclusion = ld::Atom::symbolTableNotIn;
+ ld::Atom::ContentType ctype = this->contentType();
+ if ( ctype == ld::Atom::typeLSDA )
+ inclusion = ld::Atom::symbolTableInWithRandomAutoStripLabel;
new (allocatedSpace) Atom<A>(*this, "anon", addr, size, ld::Atom::definitionRegular, ld::Atom::combineNever,
- ld::Atom::scopeTranslationUnit, this->contentType(), ld::Atom::symbolTableNotIn,
+ ld::Atom::scopeTranslationUnit, ctype, inclusion,
this->dontDeadStrip(), false, false, this->alignmentForAddress(addr));
}
p += sizeof(Atom<A>);
template <typename A>
uint32_t ImplicitSizeSection<A>::computeAtomCount(class Parser<A>& parser,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
uint32_t count = 0;
const macho_section<P>* sect = this->machoSection();
template <typename A>
uint32_t ImplicitSizeSection<A>::appendAtoms(class Parser<A>& parser, uint8_t* p,
struct Parser<A>::LabelAndCFIBreakIterator& it,
- const struct Parser<A>::CFIInfoArray&)
+ const struct Parser<A>::CFI_CU_InfoArrays&)
{
this->_beginAtoms = (Atom<A>*)p;
return true;
}
+template <typename A>
+bool CStringSection<A>::ignoreLabel(const char* label)
+{
+ return (label[0] == 'L') || (label[0] == 'l');
+}
+
template <typename A>
Atom<A>* CStringSection<A>::findAtomByAddress(pint_t addr)
{
}
template <>
-void NonLazyPointerSection<x86_64>::makeFixups(class Parser<x86_64>& parser, const struct Parser<x86_64>::CFIInfoArray&)
+void NonLazyPointerSection<x86_64>::makeFixups(class Parser<x86_64>& parser, const struct Parser<x86_64>::CFI_CU_InfoArrays&)
{
assert(0 && "x86_64 should not have non-lazy-pointer sections in .o files");
}
template <typename A>
-void NonLazyPointerSection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&)
+void NonLazyPointerSection<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&)
{
// add references for each NLP atom based on indirect symbol table
const macho_section<P>* sect = this->machoSection();
const macho_nlist<P>& targetSymbol = parser.symbolFromIndex(reloc->r_symbolnum());
target.name = parser.nameFromSymbol(targetSymbol);
target.weakImport = parser.weakImportFromSymbol(targetSymbol);
- target.addend = contentValue;
+ target.addend = (int32_t)contentValue;
}
else {
parser.findTargetFromAddressAndSectionNum(contentValue, reloc->r_symbolnum(), target);
const macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
srcAddr = sect->addr() + sreloc->r_address();
src.atom = this->findAtomByAddress(srcAddr);
+ assert(src.atom != NULL);
src.offsetInAtom = srcAddr - src.atom->_objAddress;
fixUpPtr = file().fileContent() + sect->offset() + sreloc->r_address();
uint32_t relocValue = sreloc->r_value();
switch ( sreloc->r_length() ) {
case 0:
contentValue = srcAddr + 1 + *fixUpPtr;
- target.addend = contentValue - relocValue;
+ target.addend = (int32_t)contentValue - (int32_t)relocValue;
parser.addFixups(src, ld::Fixup::kindStoreX86PCRel8, target);
break;
case 1:
contentValue = srcAddr + 2 + LittleEndian::get16(*((uint16_t*)fixUpPtr));
- target.addend = contentValue - relocValue;
+ target.addend = (int32_t)contentValue - (int32_t)relocValue;
parser.addFixups(src, ld::Fixup::kindStoreX86PCRel16, target);
break;
case 2:
contentValue = srcAddr + 4 + LittleEndian::get32(*((uint32_t*)fixUpPtr));
- target.addend = contentValue - relocValue;
+ target.addend = (int32_t)contentValue - (int32_t)relocValue;
parser.addFixups(src, ld::Fixup::kindStoreX86PCRel32, target);
break;
case 3:
if ( sreloc->r_length() != 2 )
throwf("unsupported r_length=%d for scattered vanilla reloc", sreloc->r_length());
contentValue = LittleEndian::get32(*((uint32_t*)fixUpPtr));
- target.addend = contentValue - target.atom->objectAddress();
+ target.addend = (int32_t)contentValue - (int32_t)(target.atom->objectAddress());
parser.addFixups(src, ld::Fixup::kindStoreLittleEndian32, target);
}
break;
uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress;
parser.findTargetFromAddress(sreloc->r_value(), target);
// check for addend encoded in the section content
- int32_t addend = contentValue - (sreloc->r_value() - nextRelocValue);
+ int64_t addend = (int32_t)contentValue - (int32_t)(sreloc->r_value() - nextRelocValue);
if ( addend < 0 ) {
// switch binding base on coalescing
if ( target.atom == NULL ) {
if ( reloc->r_type() != ARM_RELOC_PAIR )
instruction = LittleEndian::get32(*fixUpPtr);
if ( reloc->r_extern() ) {
- target.atom = NULL;
const macho_nlist<P>& targetSymbol = parser.symbolFromIndex(reloc->r_symbolnum());
- target.name = parser.nameFromSymbol(targetSymbol);
- target.weakImport = parser.weakImportFromSymbol(targetSymbol);
- if ( ((targetSymbol.n_type() & N_TYPE) == N_SECT) && (targetSymbol.n_desc() & N_ARM_THUMB_DEF) )
- externSymbolIsThumbDef = true;
+ // use direct reference for local symbols
+ if ( ((targetSymbol.n_type() & N_TYPE) == N_SECT) && (((targetSymbol.n_type() & N_EXT) == 0) || (parser.nameFromSymbol(targetSymbol)[0] == 'L')) ) {
+ parser.findTargetFromAddressAndSectionNum(targetSymbol.n_value(), targetSymbol.n_sect(), target);
+ }
+ else {
+ target.atom = NULL;
+ target.name = parser.nameFromSymbol(targetSymbol);
+ target.weakImport = parser.weakImportFromSymbol(targetSymbol);
+ if ( ((targetSymbol.n_type() & N_TYPE) == N_SECT) && (targetSymbol.n_desc() & N_ARM_THUMB_DEF) )
+ externSymbolIsThumbDef = true;
+ }
}
switch ( reloc->r_type() ) {
case ARM_RELOC_BR24:
throw "bad length for ARM_RELOC_VANILLA";
contentValue = LittleEndian::get32(*fixUpPtr);
if ( reloc->r_extern() ) {
- target.addend = contentValue;
+ target.addend = (int32_t)contentValue;
if ( externSymbolIsThumbDef )
target.addend &= -2; // remove thumb bit
}
if ( reloc->r_length() & 1 ) {
// high 16
dstAddr = ((instruction16 << 16) | other16);
- parser.findTargetFromAddress(dstAddr, target);
+ if ( reloc->r_extern() ) {
+ target.addend = dstAddr;
+ }
+ else {
+ parser.findTargetFromAddress(dstAddr, target);
+ if ( target.atom->isThumb() )
+ target.addend &= (-2); // remove thumb bit
+ }
parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbHigh16 : ld::Fixup::kindStoreARMHigh16), target);
}
else {
// low 16
dstAddr = (other16 << 16) | instruction16;
- parser.findTargetFromAddress(dstAddr, target);
+ if ( reloc->r_extern() ) {
+ target.addend = dstAddr;
+ }
+ else {
+ parser.findTargetFromAddress(dstAddr, target);
+ if ( target.atom->isThumb() )
+ target.addend &= (-2); // remove thumb bit
+ }
parser.addFixups(src, (isThumb ? ld::Fixup::kindStoreThumbLow16 : ld::Fixup::kindStoreARMLow16), target);
}
result = true;
uint32_t offsetInTarget;
Atom<arm>* targetAtom = parser.findAtomByAddressOrLocalTargetOfStub(sreloc->r_value(), &offsetInTarget);
// check for addend encoded in the section content
- int64_t addend = contentValue - (sreloc->r_value() - nextRelocValue);
+ int64_t addend = (int32_t)contentValue - (int32_t)(sreloc->r_value() - nextRelocValue);
if ( targetAtom->isThumb() )
addend &= -2; // remove thumb bit
// if reference to LSDA, add group subordinate fixup
else {
parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, false, targetAtom->name());
}
- parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom);
parser.addFixup(src, ld::Fixup::k2of5, ld::Fixup::kindAddAddend, offsetInTarget);
parser.addFixup(src, ld::Fixup::k3of5, ld::Fixup::kindSubtractTargetAddress, fromAtom);
parser.addFixup(src, ld::Fixup::k4of5, ld::Fixup::kindSubtractAddend, offsetInFrom-addend);
uint32_t offsetInFrom = nextRelocValue - fromAtom->_objAddress;
Atom<arm>* targetAtom = parser.findAtomByAddress(sreloc->r_value());
uint32_t offsetInTarget = sreloc->r_value() - targetAtom->_objAddress;
- //if ( targetAtom->isThumb() )
- // addend &= -2; // remove thumb bit
uint32_t instruction16;
uint32_t other16 = (nextRelocAddress & 0xFFFF);
bool isThumb;
dstAddr = ((instruction16 << 16) | other16);
else
dstAddr = (other16 << 16) | instruction16;
+ if ( targetAtom->isThumb() )
+ dstAddr &= (-2); // remove thumb bit
int32_t addend = dstAddr - (sreloc->r_value() - nextRelocValue);
if ( targetAtom->scope() == ld::Atom::scopeTranslationUnit ) {
parser.addFixup(src, ld::Fixup::k1of5, ld::Fixup::kindSetTargetAddress, targetAtom);
template <typename A>
-void Section<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFIInfoArray&)
+void Section<A>::makeFixups(class Parser<A>& parser, const struct Parser<A>::CFI_CU_InfoArrays&)
{
const macho_section<P>* sect = this->machoSection();
const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)(file().fileContent() + sect->reloff());
++r; // skip next
}
catch (const char* msg) {
- throwf("in section %s,%s reloc %u: %s", sect->segname(), sect->sectname(), r, msg);
+ throwf("in section %s,%s reloc %u: %s", sect->segname(), Section<A>::makeSectionName(sect), r, msg);
}
}
else if ( mach_o::relocatable::Parser<arm>::validFile(fileContent, false, 0) ) {
return mach_o::relocatable::Parser<arm>::hasObjC2Categories(fileContent);
}
+ else if ( mach_o::relocatable::Parser<x86>::validFile(fileContent, false, 0) ) {
+ return mach_o::relocatable::Parser<x86>::hasObjC2Categories(fileContent);
+ }
return false;
}
case ld::Fixup::kindStoreTargetAddressARMBranch24:
case ld::Fixup::kindStoreTargetAddressThumbBranch22:
if ( finalTarget.atom->isThumb() ) {
- if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 ) {
+ if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() ) {
return new Thumb2toThumbBranchIslandAtom(name, nextTarget, finalTarget);
}
else if ( opts.outputSlidable() ) {
case CPU_TYPE_ARM:
if ( ! seenThumbBranch )
return 32000000; // ARM can branch +/- 32MB
- else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 )
+ else if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() )
return 16000000; // thumb2 can branch +/- 16MB
else
return 4000000; // thumb1 can branch +/- 4MB
case CPU_TYPE_ARM:
if ( ! seenThumbBranch )
return 30*1024*1024; // 2MB of branch islands per 32MB
- else if ( opts.preferSubArchitecture() && opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 )
+ else if ( opts.preferSubArchitecture() && opts.archSupportsThumb2() )
return 14*1024*1024; // 2MB of branch islands per 16MB
else
return 3500000; // 0.5MB of branch islands per 4MB
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
static bool _s_log = false;
-static ld::Section _s_text_section("__TEXT", "__text", ld::Section::typeCode);
-
class Thumb2ToArmShimAtom : public ld::Atom {
public:
- Thumb2ToArmShimAtom(const ld::Atom* target)
- : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ Thumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
+ : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
- ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)),
+ ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
_name(NULL),
_target(target),
_fixup1(8, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
};
+class NoPICThumb2ToArmShimAtom : public ld::Atom {
+public:
+ NoPICThumb2ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
+ : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
+ ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
+ _name(NULL),
+ _target(target),
+ _fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target)
+ { asprintf((char**)&_name, "%s$shim", target->name()); }
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual bool translationUnitSource(const char** dir, const char**) const
+ { return false; }
+ virtual const char* name() const { return _name; }
+ virtual uint64_t size() const { return 12; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ // Use ARM instructions that can jump to thumb.
+ assert( ! _target->isThumb() );
+ if (_s_log) fprintf(stderr, "3 Thumb2 instruction shim to jump to %s\n", _target->name());
+ OSWriteLittleInt32(&buffer[0], 0, 0xc004f8df); // ldr ip, pc + 4
+ OSWriteLittleInt16(&buffer[4], 0, 0x4760); // bx ip
+ OSWriteLittleInt16(&buffer[6], 0, 0x46C0); // nop
+ OSWriteLittleInt32(&buffer[8], 0, 0x00000000); // .long target
+ }
+
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
+
+private:
+ const char* _name;
+ const ld::Atom* _target;
+ ld::Fixup _fixup1;
+};
+
class Thumb1ToArmShimAtom : public ld::Atom {
public:
- Thumb1ToArmShimAtom(const ld::Atom* target)
- : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ Thumb1ToArmShimAtom(const ld::Atom* target, const ld::Section& inSect)
+ : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
- ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(1)),
+ ld::Atom::symbolTableIn, false, true, false, ld::Atom::Alignment(2)),
_name(NULL),
_target(target),
_fixup1(12, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, target),
class ARMtoThumbShimAtom : public ld::Atom {
public:
- ARMtoThumbShimAtom(const ld::Atom* target)
- : ld::Atom(_s_text_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect)
+ : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)),
_name(NULL),
};
+class NoPICARMtoThumbShimAtom : public ld::Atom {
+public:
+ NoPICARMtoThumbShimAtom(const ld::Atom* target, const ld::Section& inSect)
+ : ld::Atom(inSect, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
+ ld::Atom::symbolTableIn, false, false, false, ld::Atom::Alignment(2)),
+ _name(NULL),
+ _target(target),
+ _fixup1(8, ld::Fixup::k1of1, ld::Fixup::kindStoreTargetAddressLittleEndian32, target)
+ { asprintf((char**)&_name, "%s$shim", target->name()); }
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual bool translationUnitSource(const char** dir, const char**) const
+ { return false; }
+ virtual const char* name() const { return _name; }
+ virtual uint64_t size() const { return 12; }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const {
+ // Use ARM instructions that can jump to thumb.
+ if (_s_log) fprintf(stderr, "3 ARM instruction shim to jump to %s\n", _target->name());
+ OSWriteLittleInt32(&buffer[ 0], 0, 0xe59fc000); // ldr ip, pc + 4
+ OSWriteLittleInt32(&buffer[ 4], 0, 0xe12fff1c); // bx ip
+ OSWriteLittleInt32(&buffer[ 8], 0, 0); // .long target
+ }
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixup1; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return &((ld::Fixup*)&_fixup1)[1]; }
+
+private:
+ const char* _name;
+ const ld::Atom* _target;
+ ld::Fixup _fixup1;
+};
+
+
//
void doPass(const Options& opts, ld::Internal& state)
{
- std::map<const Atom*, const Atom*> atomToThumbMap;
- std::map<const Atom*, const Atom*> thumbToAtomMap;
- std::vector<const Atom*> shims;
-
// only make branch shims in final linked images
if ( opts.outputKind() == Options::kObjectFile )
return;
if ( opts.architecture() != CPU_TYPE_ARM )
return;
- // scan to find __text section
- ld::Internal::FinalSection* textSection = NULL;
+ const bool makingKextBundle = (opts.outputKind() == Options::kKextBundle);
+
+ // scan all sections
for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
- if ( strcmp(sect->sectionName(), "__text") == 0 )
- textSection = sect;
- }
- if ( textSection == NULL )
- return;
-
- // scan __text section for branch instructions that need to switch mode
- for (std::vector<const ld::Atom*>::iterator ait=textSection->atoms.begin(); ait != textSection->atoms.end(); ++ait) {
- const ld::Atom* atom = *ait;
- const ld::Atom* target;
- for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
- switch ( fit->kind ) {
- case ld::Fixup::kindStoreTargetAddressThumbBranch22:
- extractTarget(fit, state, &target);
- if ( ! target->isThumb() ) {
- const uint8_t* fixUpLocation = atom->rawContentPointer() + fit->offsetInAtom;
- uint32_t instruction = *((uint32_t*)fixUpLocation);
- bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
- if ( is_b ) {
- fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
- const Atom* shim = NULL;
- std::map<const Atom*, const Atom*>::iterator pos = thumbToAtomMap.find(target);
- if ( pos == thumbToAtomMap.end() ) {
- if ( opts.subArchitecture() == CPU_SUBTYPE_ARM_V7 )
- shim = new Thumb2ToArmShimAtom(target);
- else
- shim = new Thumb1ToArmShimAtom(target);
- shims.push_back(shim);
- thumbToAtomMap[target] = shim;
+ std::map<const Atom*, const Atom*> atomToThumbMap;
+ std::map<const Atom*, const Atom*> thumbToAtomMap;
+ std::vector<const Atom*> shims;
+ // scan section for branch instructions that need to switch mode
+ for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ const ld::Atom* target = NULL;
+ bool targetIsProxy;
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindStoreTargetAddressThumbBranch22:
+ extractTarget(fit, state, &target);
+ targetIsProxy = (target->definition() == ld::Atom::definitionProxy);
+ if ( ! target->isThumb() ) {
+ const uint8_t* fixUpLocation = atom->rawContentPointer();
+ // <rdar://problem/9544194> don't try to scan atom for branches if atom unwilling to supply raw content
+ if ( fixUpLocation == NULL )
+ break;
+ fixUpLocation += fit->offsetInAtom;
+ uint32_t instruction = *((uint32_t*)fixUpLocation);
+ bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
+ // need shim for branch from thumb to arm, or for call to function outside kext
+ if ( is_b || (targetIsProxy && makingKextBundle) ) {
+ if ( _s_log ) fprintf(stderr, "need to add thumb->arm instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
+ const Atom* shim = NULL;
+ std::map<const Atom*, const Atom*>::iterator pos = thumbToAtomMap.find(target);
+ if ( pos == thumbToAtomMap.end() ) {
+ if ( opts.archSupportsThumb2() ) {
+ // <rdar://problem/9116044> make long-branch style shims for arm kexts
+ if ( makingKextBundle )
+ shim = new NoPICThumb2ToArmShimAtom(target, *sect);
+ else
+ shim = new Thumb2ToArmShimAtom(target, *sect);
+ }
+ else {
+ shim = new Thumb1ToArmShimAtom(target, *sect);
+ }
+ shims.push_back(shim);
+ thumbToAtomMap[target] = shim;
+ }
+ else {
+ shim = pos->second;
+ }
+ fit->binding = ld::Fixup::bindingDirectlyBound;
+ fit->u.target = shim;
}
- else {
- shim = pos->second;
- }
- fit->binding = ld::Fixup::bindingDirectlyBound;
- fit->u.target = shim;
}
- }
- break;
- case ld::Fixup::kindStoreTargetAddressARMBranch24:
- extractTarget(fit, state, &target);
- if ( target->isThumb() ) {
- const uint8_t* fixUpLocation = atom->rawContentPointer() + fit->offsetInAtom;
- uint32_t instruction = *((uint32_t*)fixUpLocation);
- bool is_b = ((instruction & 0x0F000000) == 0x0A000000);
- if ( is_b ) {
- fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
- const Atom* shim = NULL;
- std::map<const Atom*, const Atom*>::iterator pos = atomToThumbMap.find(target);
- if ( pos == atomToThumbMap.end() ) {
- shim = new ARMtoThumbShimAtom(target);
- shims.push_back(shim);
- atomToThumbMap[target] = shim;
- }
- else {
- shim = pos->second;
+ break;
+ case ld::Fixup::kindStoreTargetAddressARMBranch24:
+ extractTarget(fit, state, &target);
+ targetIsProxy = (target->definition() == ld::Atom::definitionProxy);
+ if ( target->isThumb() || (targetIsProxy && makingKextBundle) ) {
+ const uint8_t* fixUpLocation = atom->rawContentPointer();
+ // <rdar://problem/9544194> don't try to scan atom for branches if atom unwilling to supply raw content
+ if ( fixUpLocation == NULL )
+ break;
+ fixUpLocation += fit->offsetInAtom;
+ uint32_t instruction = *((uint32_t*)fixUpLocation);
+ bool is_b = ((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000);
+ // need shim for branch from arm to thumb, or for call to function outside kext
+ if ( is_b || (targetIsProxy && makingKextBundle) ) {
+ if ( _s_log ) fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name());
+ const Atom* shim = NULL;
+ std::map<const Atom*, const Atom*>::iterator pos = atomToThumbMap.find(target);
+ if ( pos == atomToThumbMap.end() ) {
+ // <rdar://problem/9116044> make long-branch style shims for arm kexts
+ if ( makingKextBundle )
+ shim = new NoPICARMtoThumbShimAtom(target, *sect);
+ else
+ shim = new ARMtoThumbShimAtom(target, *sect);
+ shims.push_back(shim);
+ atomToThumbMap[target] = shim;
+ }
+ else {
+ shim = pos->second;
+ }
+ fit->binding = ld::Fixup::bindingDirectlyBound;
+ fit->u.target = shim;
}
- fit->binding = ld::Fixup::bindingDirectlyBound;
- fit->u.target = shim;
}
- }
- break;
-
- case ld::Fixup::kindStoreARMBranch24:
- case ld::Fixup::kindStoreThumbBranch22:
- fprintf(stderr, "found branch-22 without store in %s\n", atom->name());
- break;
- default:
- break;
+ break;
+
+ //case ld::Fixup::kindStoreARMBranch24:
+ //case ld::Fixup::kindStoreThumbBranch22:
+ // Note: these fixups will only be seen if the the b/bl is to a symbol plus addend
+ // for now we don't handle making shims. If a shim is needed there will
+ // be an error later.
+ // break;
+ default:
+ break;
+ }
}
}
- }
- // append all new shims to end of __text
- textSection->atoms.insert(textSection->atoms.end(), shims.begin(), shims.end());
+ // append all new shims to end of __text
+ sect->atoms.insert(sect->atoms.end(), shims.begin(), shims.end());
+ }
}
assert(fit->binding == ld::Fixup::bindingDirectlyBound);
lsda = fit->u.target;
break;
+ case ld::Fixup::kindNoneGroupSubordinatePersonality:
+ assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+ personalityPointer = fit->u.target;
+ assert(personalityPointer->section().type() == ld::Section::typeNonLazyPointer);
+ break;
default:
break;
}
}
-
-
-void doPass(const Options& opts, ld::Internal& state)
+static void makeFinalLinkedImageCompactUnwindSection(const Options& opts, ld::Internal& state)
{
- //const bool log = false;
-
- // only make make __unwind_info in final linked images
- if ( !opts.needsUnwindInfoSection() )
- return;
-
// walk every atom and gets its unwind info
std::vector<UnwindEntry> entries;
entries.reserve(64);
}
+
+template <typename A>
+class CompactUnwindAtom : public ld::Atom {
+public:
+ CompactUnwindAtom(ld::Internal& state,const ld::Atom* funcAtom,
+ uint32_t startOffset, uint32_t len, uint32_t cui);
+ ~CompactUnwindAtom() {}
+
+ virtual const ld::File* file() const { return NULL; }
+ virtual bool translationUnitSource(const char** dir, const char**) const
+ { return false; }
+ virtual const char* name() const { return "compact unwind info"; }
+ virtual uint64_t size() const { return sizeof(macho_compact_unwind_entry<P>); }
+ virtual uint64_t objectAddress() const { return 0; }
+ virtual void copyRawContent(uint8_t buffer[]) const;
+ virtual void setScope(Scope) { }
+ virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; }
+ virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; }
+
+private:
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+
+ const ld::Atom* _atom;
+ const uint32_t _startOffset;
+ const uint32_t _len;
+ const uint32_t _compactUnwindInfo;
+ std::vector<ld::Fixup> _fixups;
+
+ static ld::Fixup::Kind _s_pointerKind;
+ static ld::Fixup::Kind _s_pointerStoreKind;
+ static ld::Section _s_section;
+};
+
+
+template <typename A>
+ld::Section CompactUnwindAtom<A>::_s_section("__LD", "__compact_unwind", ld::Section::typeDebug);
+
+template <> ld::Fixup::Kind CompactUnwindAtom<x86>::_s_pointerKind = ld::Fixup::kindStoreLittleEndian32;
+template <> ld::Fixup::Kind CompactUnwindAtom<x86>::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian32;
+template <> ld::Fixup::Kind CompactUnwindAtom<x86_64>::_s_pointerKind = ld::Fixup::kindStoreLittleEndian64;
+template <> ld::Fixup::Kind CompactUnwindAtom<x86_64>::_s_pointerStoreKind = ld::Fixup::kindStoreTargetAddressLittleEndian64;
+
+template <typename A>
+CompactUnwindAtom<A>::CompactUnwindAtom(ld::Internal& state,const ld::Atom* funcAtom, uint32_t startOffset,
+ uint32_t len, uint32_t cui)
+ : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
+ ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified,
+ symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)),
+ _atom(funcAtom), _startOffset(startOffset), _len(len), _compactUnwindInfo(cui)
+{
+ _fixups.push_back(ld::Fixup(macho_compact_unwind_entry<P>::codeStartFieldOffset(), ld::Fixup::k1of3, ld::Fixup::kindSetTargetAddress, funcAtom));
+ _fixups.push_back(ld::Fixup(macho_compact_unwind_entry<P>::codeStartFieldOffset(), ld::Fixup::k2of3, ld::Fixup::kindAddAddend, _startOffset));
+ _fixups.push_back(ld::Fixup(macho_compact_unwind_entry<P>::codeStartFieldOffset(), ld::Fixup::k3of3, _s_pointerKind));
+ // see if atom has subordinate personality function or lsda
+ for (ld::Fixup::iterator fit = funcAtom->fixupsBegin(), end=funcAtom->fixupsEnd(); fit != end; ++fit) {
+ switch ( fit->kind ) {
+ case ld::Fixup::kindNoneGroupSubordinatePersonality:
+ assert(fit->binding == ld::Fixup::bindingsIndirectlyBound);
+ _fixups.push_back(ld::Fixup(macho_compact_unwind_entry<P>::personalityFieldOffset(), ld::Fixup::k1of1, _s_pointerStoreKind, state.indirectBindingTable[fit->u.bindingIndex]));
+ break;
+ case ld::Fixup::kindNoneGroupSubordinateLSDA:
+ assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+ _fixups.push_back(ld::Fixup(macho_compact_unwind_entry<P>::lsdaFieldOffset(), ld::Fixup::k1of1, _s_pointerStoreKind, fit->u.target));
+ break;
+ default:
+ break;
+ }
+ }
+
+}
+
+template <typename A>
+void CompactUnwindAtom<A>::copyRawContent(uint8_t buffer[]) const
+{
+ macho_compact_unwind_entry<P>* buf = (macho_compact_unwind_entry<P>*)buffer;
+ buf->set_codeStart(0);
+ buf->set_codeLen(_len);
+ buf->set_compactUnwindInfo(_compactUnwindInfo);
+ buf->set_personality(0);
+ buf->set_lsda(0);
+}
+
+
+static void makeCompactUnwindAtom(const Options& opts, ld::Internal& state, const ld::Atom* atom,
+ uint32_t startOffset, uint32_t endOffset, uint32_t cui)
+{
+ switch ( opts.architecture() ) {
+ case CPU_TYPE_X86_64:
+ state.addAtom(*new CompactUnwindAtom<x86_64>(state, atom, startOffset, endOffset-startOffset, cui));
+ break;
+ case CPU_TYPE_I386:
+ state.addAtom(*new CompactUnwindAtom<x86>(state, atom, startOffset, endOffset-startOffset, cui));
+ break;
+ }
+}
+
+static void makeRelocateableCompactUnwindSection(const Options& opts, ld::Internal& state)
+{
+ // can't add CompactUnwindAtom atoms will iterating, so pre-scan
+ std::vector<const ld::Atom*> atomsWithUnwind;
+ for (std::vector<ld::Internal::FinalSection*>::const_iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ if ( atom->beginUnwind() != atom->endUnwind() )
+ atomsWithUnwind.push_back(atom);
+ }
+ }
+ // make one CompactUnwindAtom for each compact unwind range in each atom
+ for (std::vector<const ld::Atom*>::iterator it = atomsWithUnwind.begin(); it != atomsWithUnwind.end(); ++it) {
+ const ld::Atom* atom = *it;
+ uint32_t lastOffset = 0;
+ uint32_t lastCUE = 0;
+ bool first = true;
+ for (ld::Atom::UnwindInfo::iterator uit=atom->beginUnwind(); uit != atom->endUnwind(); ++uit) {
+ if ( !first ) {
+ makeCompactUnwindAtom(opts, state, atom, lastOffset, uit->startOffset, lastCUE);
+ }
+ lastOffset = uit->startOffset;
+ lastCUE = uit->unwindInfo;
+ first = false;
+ }
+ makeCompactUnwindAtom(opts, state, atom, lastOffset, (uint32_t)atom->size(), lastCUE);
+ }
+}
+
+
+void doPass(const Options& opts, ld::Internal& state)
+{
+ if ( opts.outputKind() == Options::kObjectFile )
+ makeRelocateableCompactUnwindSection(opts, state);
+
+ else if ( opts.needsUnwindInfoSection() )
+ makeFinalLinkedImageCompactUnwindSection(opts, state);
+}
+
+
} // namespace compact_unwind
} // namespace passes
} // namespace ld
// set "willRemoved" bit on any unused explicit when -dead_strip_dylibs is used
if ( opts.deadStripDylibs() && !aDylib->providedExportAtom() )
aDylib->setWillBeRemoved(true);
- }
-
-
+ }
// remove unused dylibs
state.dylibs.erase(std::remove_if(state.dylibs.begin(), state.dylibs.end(), WillBeUsed()), state.dylibs.end());
+
+ // <rdar://problem/9441273> automatically weak-import dylibs when all symbols from it are weak-imported
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ const ld::Atom* target = NULL;
+ bool targetIsWeakImport = false;
+ for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
+ if ( fit->firstInCluster() )
+ target = NULL;
+ switch ( fit->binding ) {
+ case ld::Fixup::bindingsIndirectlyBound:
+ target = state.indirectBindingTable[fit->u.bindingIndex];
+ targetIsWeakImport = fit->weakImport;
+ break;
+ case ld::Fixup::bindingDirectlyBound:
+ target = fit->u.target;
+ targetIsWeakImport = fit->weakImport;
+ break;
+ default:
+ break;
+ }
+ if ( (target != NULL) && (target->definition() == ld::Atom::definitionProxy) ) {
+ ld::Atom::WeakImportState curWI = target->weakImportState();
+ if ( curWI == ld::Atom::weakImportUnset ) {
+ // first use of this proxy, set weak-import based on this usage
+ (const_cast<ld::Atom*>(target))->setWeakImportState(targetIsWeakImport);
+ }
+ else {
+ // proxy already has weak-importness set, check for weakness mismatch
+ bool curIsWeakImport = (curWI == ld::Atom::weakImportTrue);
+ if ( curIsWeakImport != targetIsWeakImport ) {
+ // found mismatch
+ switch ( opts.weakReferenceMismatchTreatment() ) {
+ case Options::kWeakReferenceMismatchError:
+ throwf("mismatching weak references for symbol: %s", target->name());
+ case Options::kWeakReferenceMismatchWeak:
+ (const_cast<ld::Atom*>(target))->setWeakImportState(true);
+ break;
+ case Options::kWeakReferenceMismatchNonWeak:
+ (const_cast<ld::Atom*>(target))->setWeakImportState(false);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
}
case ld::Fixup::kindStoreX86PCRel32GOT:
*optimizable = false;
return true;
+ case ld::Fixup::kindNoneGroupSubordinatePersonality:
+ *optimizable = false;
+ return true;
default:
break;
}
if ( opts.outputKind() == Options::kObjectFile )
return;
- // walk all atoms and fixups looking for stubable references
- // don't create stubs inline because that could invalidate the sections walk
+ // walk all atoms and fixups looking for GOT-able references
+ // 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*,ld::Atom*> gotMap;
std::map<const ld::Atom*,bool> weakImportMap;
const ld::Atom* atom = *ait;
bool atomUsesGOT = false;
const ld::Atom* targetOfGOT = NULL;
+ bool targetIsWeakImport = false;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
if ( fit->firstInCluster() )
targetOfGOT = NULL;
switch ( fit->binding ) {
case ld::Fixup::bindingsIndirectlyBound:
targetOfGOT = internal.indirectBindingTable[fit->u.bindingIndex];
+ targetIsWeakImport = fit->weakImport;
break;
case ld::Fixup::bindingDirectlyBound:
targetOfGOT = fit->u.target;
+ targetIsWeakImport = fit->weakImport;
break;
default:
break;
std::map<const ld::Atom*,bool>::iterator pos = weakImportMap.find(targetOfGOT);
if ( pos == weakImportMap.end() ) {
// target not in weakImportMap, so add
- weakImportMap[targetOfGOT] = fit->weakImport;
- // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
- const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(targetOfGOT->file());
- if ( dylib != NULL ) {
- if ( fit->weakImport )
- (const_cast<ld::dylib::File*>(dylib))->setUsingWeakImportedSymbols();
- else
- (const_cast<ld::dylib::File*>(dylib))->setUsingNonWeakImportedSymbols();
- }
+ if ( log ) fprintf(stderr, "weakImportMap[%s] = %d\n", targetOfGOT->name(), targetIsWeakImport);
+ weakImportMap[targetOfGOT] = targetIsWeakImport;
}
else {
// target in weakImportMap, check for weakness mismatch
- if ( pos->second != fit->weakImport ) {
+ if ( pos->second != targetIsWeakImport ) {
// found mismatch
switch ( opts.weakReferenceMismatchTreatment() ) {
case Options::kWeakReferenceMismatchError:
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _methodCount(0)
{
unsigned int fixupCount = 0;
+ std::set<const ld::Atom*> baseMethodListMethodNameAtoms;
// if base class has method list, then associate new method list with file defining class
if ( baseMethodList != NULL ) {
_file = baseMethodList->file();
_methodCount = MethodList<A>::count(state, baseMethodList);
deadAtoms.insert(baseMethodList);
fixupCount = baseMethodList->fixupsEnd() - baseMethodList->fixupsBegin();
+ for (ld::Fixup::iterator fit=baseMethodList->fixupsBegin(); fit != baseMethodList->fixupsEnd(); ++fit) {
+ if ( (fit->offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) {
+ assert(fit->binding == ld::Fixup::bindingsIndirectlyBound && "malformed method list");
+ const ld::Atom* target = state.indirectBindingTable[fit->u.bindingIndex];
+ assert(target->contentType() == ld::Atom::typeCString && "malformed method list");
+ baseMethodListMethodNameAtoms.insert(target);
+ }
+ }
}
for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) {
const ld::Atom* categoryMethodListAtom;
// copy fixups and adjust offsets (in reverse order to simulator objc runtime)
_fixups.reserve(fixupCount);
uint32_t slide = 0;
+ std::set<const ld::Atom*> categoryMethodNameAtoms;
for (std::vector<const ld::Atom*>::const_reverse_iterator rit=categories->rbegin(); rit != categories->rend(); ++rit) {
const ld::Atom* categoryMethodListAtom;
if ( meta )
ld::Fixup fixup = *fit;
fixup.offsetInAtom += slide;
_fixups.push_back(fixup);
- //if ( fixup.binding == ld::Fixup::bindingDirectlyBound )
- // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name());
+ if ( (fixup.offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) {
+ // <rdar://problem/8642343> warning when a method is overridden in a category in the same link unit
+ assert(fixup.binding == ld::Fixup::bindingsIndirectlyBound && "malformed category method list");
+ const ld::Atom* target = state.indirectBindingTable[fixup.u.bindingIndex];
+ assert(target->contentType() == ld::Atom::typeCString && "malformed method list");
+ // this objc pass happens after cstrings are coalesced, so we can just compare the atom addres instead of its content
+ if ( baseMethodListMethodNameAtoms.count(target) != 0 ) {
+ warning("%s method '%s' in category from %s overrides method from class in %s",
+ (meta ? "meta" : "instance"), target->rawContentPointer(),
+ categoryMethodListAtom->file()->path(), baseMethodList->file()->path() );
+ }
+ if ( categoryMethodNameAtoms.count(target) != 0 ) {
+ warning("%s method '%s' in category from %s conflicts with same method from another category",
+ (meta ? "meta" : "instance"), target->rawContentPointer(),
+ categoryMethodListAtom->file()->path());
+ }
+ categoryMethodNameAtoms.insert(target);
+ }
}
slide += 3*sizeof(pint_t) * MethodList<A>::count(state, categoryMethodListAtom);
}
if ( pos == _nameTable.end() )
_nameTable[name] = atom;
else {
- _nameTable[name] = NULL; // collision, denote with NULL
+ const ld::Atom* existing = _nameTable[name];
+ if ( existing != NULL ) {
+ _nameCollisionAtoms.push_back(existing);
+ _nameTable[name] = NULL; // collision, denote with NULL
+ }
_nameCollisionAtoms.push_back(atom);
}
}
}
}
}
+ if ( _s_log ) {
+ fprintf(stderr, "buildNameTable() _nameTable:\n");
+ for(NameToAtom::iterator it=_nameTable.begin(); it != _nameTable.end(); ++it)
+ fprintf(stderr, " %p <- %s\n", it->second, it->first);
+ fprintf(stderr, "buildNameTable() _nameCollisionAtoms:\n");
+ for(std::vector<const ld::Atom*>::iterator it=_nameCollisionAtoms.begin(); it != _nameCollisionAtoms.end(); ++it)
+ fprintf(stderr, " %p, %s\n", *it, (*it)->name());
+ }
}
}
if ( pos->second == NULL ) {
// name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
+ if ( ( orderedSymbol.objectFileName == NULL) && _options.printOrderFileStatistics() ) {
+ warning("%s specified in order_file but it exists in multiple .o files. "
+ "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName);
+ }
for (std::vector<const ld::Atom*>::iterator it=_nameCollisionAtoms.begin(); it != _nameCollisionAtoms.end(); it++) {
const ld::Atom* atom = *it;
if ( strcmp(atom->name(), orderedSymbol.symbolName) == 0 ) {
if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
- if ( _options.printOrderFileStatistics() )
- warning("%s specified in order_file but it exists in multiple .o files. "
- "Prefix symbol with .o filename in order_file to disambiguate", orderedSymbol.symbolName);
return atom;
}
}
forLazyDylib = true;
bool stubToResolver = (target.contentType() == ld::Atom::typeResolver);
+ if ( usingCompressedLINKEDIT() && !forLazyDylib ) {
+ if ( _internal->compressedFastBinderProxy == NULL )
+ throwf("symbol dyld_stub_binder not found (normally in libSystem.dylib). Needed to perform lazy binding to function %s", target.name());
+ }
+
switch ( _architecture ) {
case CPU_TYPE_POWERPC:
if ( _pic )
if ( pos == weakImportMap.end() ) {
// target not in weakImportMap, so add
weakImportMap[stubableTargetOfFixup] = fit->weakImport;
- // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
- const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(stubableTargetOfFixup->file());
- if ( dylib != NULL ) {
- if ( fit->weakImport )
- (const_cast<ld::dylib::File*>(dylib))->setUsingWeakImportedSymbols();
- else
- (const_cast<ld::dylib::File*>(dylib))->setUsingNonWeakImportedSymbols();
- }
}
else {
// target in weakImportMap, check for weakness mismatch
if ( !_options.makeCompressedDyldInfo() && (state.classicBindingHelper == NULL) )
throw "symbol dyld_stub_binding_helper not found, normally in crt1.o/dylib1.o/bundle1.o";
- // disable close stubs when branch islands might be needed
- if ( (_architecture == CPU_TYPE_ARM) && (codeSize > 4*1024*1024) )
- _largeText = true;
+ // disable arm close stubs in some cases
+ if ( _architecture == CPU_TYPE_ARM ) {
+ if ( codeSize > 4*1024*1024 )
+ _largeText = true;
+ else {
+ for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
+ ld::Internal::FinalSection* sect = *sit;
+ if ( sect->type() == ld::Section::typeMachHeader )
+ continue;
+ if ( strcmp(sect->segmentName(), "__TEXT") == 0) {
+ for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
+ const ld::Atom* atom = *ait;
+ if ( atom->alignment().powerOf2 > 10 ) {
+ // overaligned section means might not be able to keep closestubs sect pushed to end of __TEXT
+ //warning("alignment 1<<%d in atom %s in section %s disables close stubs optimization",
+ // atom->alignment().powerOf2, atom->name(), sect->segmentName());
+ _largeText = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
// make stub atoms
for (std::map<const ld::Atom*,ld::Atom*>::iterator it = stubFor.begin(); it != stubFor.end(); ++it) {
if ( pos == weakImportMap.end() ) {
// target not in weakImportMap, so add
weakImportMap[it->targetOfTLV] = it->fixupWithTarget->weakImport;
- // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
- const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(it->targetOfTLV->file());
- if ( dylib != NULL ) {
- if ( it->fixupWithTarget->weakImport )
- (const_cast<ld::dylib::File*>(dylib))->setUsingWeakImportedSymbols();
- else
- (const_cast<ld::dylib::File*>(dylib))->setUsingNonWeakImportedSymbols();
- }
}
else {
// target in weakImportMap, check for weakness mismatch
static bool sShowSection = true;
static bool sShowDefinitionKind = true;
static bool sShowCombineKind = true;
+static bool sShowLineInfo = true;
-static cpu_type_t sPreferredArch = CPU_TYPE_I386;
+static cpu_type_t sPreferredArch = 0xFFFFFFFF;
static cpu_subtype_t sPreferredSubArch = 0xFFFFFFFF;
static const char* sMatchName = NULL;
static int sPrintRestrict;
if ( sShowSection )
printf("section: %s,%s\n", atom.section().segmentName(), atom.section().sectionName());
if ( atom.beginUnwind() != atom.endUnwind() ) {
- printf("unwind: 0x%08X\n", atom.beginUnwind()->unwindInfo);
+ uint32_t lastOffset = 0;
+ uint32_t lastCUE = 0;
+ bool first = true;
+ const char* label = "unwind:";
+ for (ld::Atom::UnwindInfo::iterator it=atom.beginUnwind(); it != atom.endUnwind(); ++it) {
+ if ( !first ) {
+ printf("%s 0x%08X -> 0x%08X: 0x%08X\n", label, lastOffset, it->startOffset, lastCUE);
+ label = " ";
+ }
+ lastOffset = it->startOffset;
+ lastCUE = it->unwindInfo;
+ first = false;
+ }
+ printf("%s 0x%08X -> 0x%08X: 0x%08X\n", label, lastOffset, (uint32_t)atom.size(), lastCUE);
}
if ( atom.contentType() == ld::Atom::typeCString ) {
uint8_t buffer[atom.size()+2];
}
}
}
- if ( atom.beginLineInfo() != atom.endLineInfo() ) {
- printf("line info:\n");
- for (ld::Atom::LineInfo::iterator it = atom.beginLineInfo(); it != atom.endLineInfo(); ++it) {
- printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName);
+ if ( sShowLineInfo ) {
+ if ( atom.beginLineInfo() != atom.endLineInfo() ) {
+ printf("line info:\n");
+ for (ld::Atom::LineInfo::iterator it = atom.beginLineInfo(); it != atom.endLineInfo(); ++it) {
+ printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName);
+ }
}
}
-
+
printf("\n");
}
if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
const struct fat_header* fh = (struct fat_header*)p;
const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
- for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
- if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) {
- if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) {
- p = p + OSSwapBigToHostInt32(archs[i].offset);
- mh = (struct mach_header*)p;
- fileLen = OSSwapBigToHostInt32(archs[i].size);
- foundFatSlice = true;
- break;
+ if ( (uint32_t)sPreferredArch == 0xFFFFFFFF ) {
+ // just dump first slice of fat .o file
+ if ( OSSwapBigToHostInt32(fh->nfat_arch) > 0 ) {
+ p = p + OSSwapBigToHostInt32(archs[0].offset);
+ mh = (struct mach_header*)p;
+ fileLen = OSSwapBigToHostInt32(archs[0].size);
+ sPreferredArch = OSSwapBigToHostInt32(archs[0].cputype);
+ sPreferredSubArch = OSSwapBigToHostInt32(archs[0].cpusubtype);
+ foundFatSlice = true;
+ }
+ }
+ else {
+ for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
+ if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) {
+ if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) {
+ p = p + OSSwapBigToHostInt32(archs[i].offset);
+ mh = (struct mach_header*)p;
+ fileLen = OSSwapBigToHostInt32(archs[i].size);
+ foundFatSlice = true;
+ break;
+ }
}
}
}
else if ( strcmp(arg, "-no_combine") == 0 ) {
sShowCombineKind = false;
}
+ else if ( strcmp(arg, "-no_line_info") == 0 ) {
+ sShowLineInfo = false;
+ }
else if ( strcmp(arg, "-arch") == 0 ) {
const char* arch = ++i<argc? argv[i]: "";
if ( strcmp(arch, "ppc64") == 0 )
sPreferredArch = CPU_TYPE_I386;
else if ( strcmp(arch, "x86_64") == 0 )
sPreferredArch = CPU_TYPE_X86_64;
- else if ( strcmp(arch, "arm") == 0 )
- sPreferredArch = CPU_TYPE_ARM;
- else if ( strcmp(arch, "armv4t") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V4T;
- }
- else if ( strcmp(arch, "armv5") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V5TEJ;
- }
- else if ( strcmp(arch, "armv6") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V6;
- }
- else if ( strcmp(arch, "armv7") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V7;
+ else {
+ bool found = false;
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( strcmp(t->subTypeName,arch) == 0 ) {
+ sPreferredArch = CPU_TYPE_ARM;
+ sPreferredSubArch = t->subType;
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ throwf("unknown architecture %s", arch);
}
- else
- throwf("unknown architecture %s", arch);
}
else if ( strcmp(arg, "-only") == 0 ) {
sMatchName = ++i<argc? argv[i]: NULL;
static bool printSharedRegion = false;
static bool printFunctionStarts = false;
static bool printDylibs = false;
-static cpu_type_t sPreferredArch = CPU_TYPE_I386;
+static cpu_type_t sPreferredArch = 0;
static cpu_type_t sPreferredSubArch = 0;
{
public:
static bool validFile(const uint8_t* fileContent);
- static DyldInfoPrinter<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path)
- { return new DyldInfoPrinter<A>(fileContent, fileLength, path); }
+ static DyldInfoPrinter<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch)
+ { return new DyldInfoPrinter<A>(fileContent, fileLength, path, printArch); }
virtual ~DyldInfoPrinter() {}
typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
- DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path);
+ DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch);
void printRebaseInfo();
void printRebaseInfoOpcodes();
void printBindingInfo();
}
template <typename A>
-DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path)
+DyldInfoPrinter<A>::DyldInfoPrinter(const uint8_t* fileContent, uint32_t fileLength, const char* path, bool printArch)
: fHeader(NULL), fLength(fileLength),
fStrings(NULL), fStringsEnd(NULL), fSymbols(NULL), fSymbolCount(0), fInfo(NULL),
fSharedRegionInfo(NULL), fFunctionStartsInfo(NULL),
cmd = (const macho_load_command<P>*)endOfCmd;
}
+ if ( printArch ) {
+ switch ( fHeader->cputype() ) {
+ case CPU_TYPE_I386:
+ printf("for arch i386:\n");
+ break;
+ case CPU_TYPE_X86_64:
+ printf("for arch x86_64:\n");
+ break;
+ case CPU_TYPE_POWERPC:
+ printf("for arch ppc:\n");
+ break;
+ case CPU_TYPE_ARM:
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( (cpu_subtype_t)fHeader->cpusubtype() == t->subType) {
+ printf("for arch %s:\n", t->subTypeName);
+ break;
+ }
+ }
+ }
+ }
+
+
if ( printRebase ) {
if ( fInfo != NULL )
printRebaseInfo();
case 4:
kindStr = "32-bit offset to IMPORT";
break;
+ case 5:
+ kindStr = "thumb2 movw";
+ break;
+ case 6:
+ kindStr = "ARM movw";
+ break;
+ case 0x10:
+ kindStr = "thumb2 movt low high 4 bits=0";
+ break;
+ case 0x11:
+ kindStr = "thumb2 movt low high 4 bits=1";
+ break;
+ case 0x12:
+ kindStr = "thumb2 movt low high 4 bits=2";
+ break;
+ case 0x13:
+ kindStr = "thumb2 movt low high 4 bits=3";
+ break;
+ case 0x14:
+ kindStr = "thumb2 movt low high 4 bits=4";
+ break;
+ case 0x15:
+ kindStr = "thumb2 movt low high 4 bits=5";
+ break;
+ case 0x16:
+ kindStr = "thumb2 movt low high 4 bits=6";
+ break;
+ case 0x17:
+ kindStr = "thumb2 movt low high 4 bits=7";
+ break;
+ case 0x18:
+ kindStr = "thumb2 movt low high 4 bits=8";
+ break;
+ case 0x19:
+ kindStr = "thumb2 movt low high 4 bits=9";
+ break;
+ case 0x1A:
+ kindStr = "thumb2 movt low high 4 bits=0xA";
+ break;
+ case 0x1B:
+ kindStr = "thumb2 movt low high 4 bits=0xB";
+ break;
+ case 0x1C:
+ kindStr = "thumb2 movt low high 4 bits=0xC";
+ break;
+ case 0x1D:
+ kindStr = "thumb2 movt low high 4 bits=0xD";
+ break;
+ case 0x1E:
+ kindStr = "thumb2 movt low high 4 bits=0xE";
+ break;
+ case 0x1F:
+ kindStr = "thumb2 movt low high 4 bits=0xF";
+ break;
}
uint64_t address = 0;
uint64_t delta = 0;
template <typename A>
const char* DyldInfoPrinter<A>::symbolNameForAddress(uint64_t addr)
{
- // find exact match in globals
- const macho_nlist<P>* lastExport = &fSymbols[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()];
- for (const macho_nlist<P>* sym = &fSymbols[fDynamicSymbolTable->iextdefsym()]; sym < lastExport; ++sym) {
- if ( (sym->n_value() == addr) && ((sym->n_type() & N_TYPE) == N_SECT) && ((sym->n_type() & N_STAB) == 0) ) {
- return &fStrings[sym->n_strx()];
+ 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 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 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()];
+ 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()];
+ }
}
}
-
return "?";
}
size_t size = OSSwapBigToHostInt32(archs[i].size);
cpu_type_t cputype = OSSwapBigToHostInt32(archs[i].cputype);
cpu_type_t cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype);
- if ( (cputype == sPreferredArch)
- && ((sPreferredSubArch==0) || (sPreferredSubArch==cpusubtype)) ) {
+ if ( ((cputype == sPreferredArch)
+ && ((sPreferredSubArch==0) || (sPreferredSubArch==cpusubtype)))
+ || (sPreferredArch == 0) ) {
switch(cputype) {
case CPU_TYPE_POWERPC:
if ( DyldInfoPrinter<ppc>::validFile(p + offset) )
- DyldInfoPrinter<ppc>::make(p + offset, size, path);
+ DyldInfoPrinter<ppc>::make(p + offset, size, path, (sPreferredArch == 0));
else
throw "in universal file, ppc slice does not contain ppc mach-o";
break;
case CPU_TYPE_I386:
if ( DyldInfoPrinter<x86>::validFile(p + offset) )
- DyldInfoPrinter<x86>::make(p + offset, size, path);
+ DyldInfoPrinter<x86>::make(p + offset, size, path, (sPreferredArch == 0));
else
throw "in universal file, i386 slice does not contain i386 mach-o";
break;
case CPU_TYPE_POWERPC64:
if ( DyldInfoPrinter<ppc64>::validFile(p + offset) )
- DyldInfoPrinter<ppc64>::make(p + offset, size, path);
+ DyldInfoPrinter<ppc64>::make(p + offset, size, path, (sPreferredArch == 0));
else
throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
break;
case CPU_TYPE_X86_64:
if ( DyldInfoPrinter<x86_64>::validFile(p + offset) )
- DyldInfoPrinter<x86_64>::make(p + offset, size, path);
+ DyldInfoPrinter<x86_64>::make(p + offset, size, path, (sPreferredArch == 0));
else
throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
break;
case CPU_TYPE_ARM:
- if ( DyldInfoPrinter<arm>::validFile(p + offset) )
- DyldInfoPrinter<arm>::make(p + offset, size, path);
+ if ( DyldInfoPrinter<arm>::validFile(p + offset) )
+ DyldInfoPrinter<arm>::make(p + offset, size, path, (sPreferredArch == 0));
else
throw "in universal file, arm slice does not contain arm mach-o";
break;
}
}
else if ( DyldInfoPrinter<x86>::validFile(p) ) {
- DyldInfoPrinter<x86>::make(p, length, path);
+ DyldInfoPrinter<x86>::make(p, length, path, false);
}
else if ( DyldInfoPrinter<ppc>::validFile(p) ) {
- DyldInfoPrinter<ppc>::make(p, length, path);
+ DyldInfoPrinter<ppc>::make(p, length, path, false);
}
else if ( DyldInfoPrinter<ppc64>::validFile(p) ) {
- DyldInfoPrinter<ppc64>::make(p, length, path);
+ DyldInfoPrinter<ppc64>::make(p, length, path, false);
}
else if ( DyldInfoPrinter<x86_64>::validFile(p) ) {
- DyldInfoPrinter<x86_64>::make(p, length, path);
+ DyldInfoPrinter<x86_64>::make(p, length, path, false);
}
else if ( DyldInfoPrinter<arm>::validFile(p) ) {
- DyldInfoPrinter<arm>::make(p, length, path);
+ DyldInfoPrinter<arm>::make(p, length, path, false);
}
else {
throw "not a known file type";
sPreferredArch = CPU_TYPE_I386;
else if ( strcmp(arch, "x86_64") == 0 )
sPreferredArch = CPU_TYPE_X86_64;
- else if ( strcmp(arch, "arm") == 0 )
- sPreferredArch = CPU_TYPE_ARM;
- else if ( strcmp(arch, "armv4t") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V4T;
- }
- else if ( strcmp(arch, "armv5") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V5TEJ;
- }
- else if ( strcmp(arch, "armv6") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V6;
- }
- else if ( strcmp(arch, "armv7") == 0 ) {
- sPreferredArch = CPU_TYPE_ARM;
- sPreferredSubArch = CPU_SUBTYPE_ARM_V7;
+ else {
+ bool found = false;
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( strcmp(t->subTypeName,arch) == 0 ) {
+ sPreferredArch = CPU_TYPE_ARM;
+ sPreferredSubArch = t->subType;
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ throwf("unknown architecture %s", arch);
}
- else
- throwf("unknown architecture %s", arch);
}
else if ( strcmp(arg, "-rebase") == 0 ) {
printRebase = true;
return result;
}
+
+static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
+{
+ int64_t result = 0;
+ int bit = 0;
+ uint8_t byte;
+ do {
+ if (p == end)
+ throwf("malformed sleb128");
+ byte = *p++;
+ result |= ((byte & 0x7f) << bit);
+ bit += 7;
+ } while (byte & 0x80);
+ // sign extend negative numbers
+ if ( (byte & 0x40) != 0 )
+ result |= (-1LL) << bit;
+ return result;
+}
+
+
template <typename A>
class MachOChecker
{
void checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect);
uint8_t loadCommandSizeMask();
void checkSymbolTable();
+ void checkInitTerms();
void checkIndirectSymbolTable();
void checkRelocations();
void checkExternalReloation(const macho_relocation_info<P>* reloc);
bool addressInWritableSegment(pint_t address);
bool hasTextRelocInRange(pint_t start, pint_t end);
pint_t segStartAddress(uint8_t segIndex);
-
+ bool addressIsRebaseSite(pint_t addr);
+ bool addressIsBindingSite(pint_t addr);
+ pint_t getInitialStackPointer(const macho_thread_command<P>*);
+ pint_t getEntryPoint(const macho_thread_command<P>*);
+
+
+
const char* fPath;
const macho_header<P>* fHeader;
uint32_t fLength;
const macho_relocation_info<P>* fExternalRelocations;
uint32_t fExternalRelocationsCount;
bool fWriteableSegmentWithAddrOver4G;
+ bool fSlidableImage;
const macho_segment_command<P>* fFirstSegment;
const macho_segment_command<P>* fFirstWritableSegment;
+ const macho_segment_command<P>* fTEXTSegment;
const macho_dyld_info_command<P>* fDyldInfo;
uint32_t fSectionCount;
std::vector<const macho_segment_command<P>*>fSegments;
template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; }
template <> uint8_t MachOChecker<arm>::loadCommandSizeMask() { return 0x03; }
+
+template <>
+ppc::P::uint_t MachOChecker<ppc>::getInitialStackPointer(const macho_thread_command<ppc::P>* threadInfo)
+{
+ return threadInfo->thread_register(3);
+}
+
+template <>
+ppc64::P::uint_t MachOChecker<ppc64>::getInitialStackPointer(const macho_thread_command<ppc64::P>* threadInfo)
+{
+ return threadInfo->thread_register(3);
+}
+
+template <>
+x86::P::uint_t MachOChecker<x86>::getInitialStackPointer(const macho_thread_command<x86::P>* threadInfo)
+{
+ return threadInfo->thread_register(7);
+}
+
+template <>
+x86_64::P::uint_t MachOChecker<x86_64>::getInitialStackPointer(const macho_thread_command<x86_64::P>* threadInfo)
+{
+ return threadInfo->thread_register(7);
+}
+
+template <>
+arm::P::uint_t MachOChecker<arm>::getInitialStackPointer(const macho_thread_command<arm::P>* threadInfo)
+{
+ return threadInfo->thread_register(13);
+}
+
+
+
+
+
+template <>
+ppc::P::uint_t MachOChecker<ppc>::getEntryPoint(const macho_thread_command<ppc::P>* threadInfo)
+{
+ return threadInfo->thread_register(0);
+}
+
+template <>
+ppc64::P::uint_t MachOChecker<ppc64>::getEntryPoint(const macho_thread_command<ppc64::P>* threadInfo)
+{
+ return threadInfo->thread_register(0);
+}
+
+template <>
+x86::P::uint_t MachOChecker<x86>::getEntryPoint(const macho_thread_command<x86::P>* threadInfo)
+{
+ return threadInfo->thread_register(10);
+}
+
+template <>
+x86_64::P::uint_t MachOChecker<x86_64>::getEntryPoint(const macho_thread_command<x86_64::P>* threadInfo)
+{
+ return threadInfo->thread_register(16);
+}
+
+template <>
+arm::P::uint_t MachOChecker<arm>::getEntryPoint(const macho_thread_command<arm::P>* threadInfo)
+{
+ return threadInfo->thread_register(15);
+}
+
+
template <typename A>
MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path)
: fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0),
fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0),
- fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fDyldInfo(NULL), fSectionCount(0)
+ fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fFirstSegment(NULL), fFirstWritableSegment(NULL),
+ fTEXTSegment(NULL), fDyldInfo(NULL), fSectionCount(0)
{
// sanity check
if ( ! validFile(fileContent) )
checkRelocations();
checkSymbolTable();
+
+ checkInitTerms();
}
throw "invalid bits in mach_header flags";
if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) )
throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
+
+ switch ( fHeader->filetype() ) {
+ case MH_EXECUTE:
+ fSlidableImage = ( flags & MH_PIE );
+ break;
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ fSlidableImage = true;
+ break;
+ default:
+ throw "not a mach-o file type supported by this tool";
+ }
}
template <typename A>
{
// check that all load commands fit within the load command space file
const macho_encryption_info_command<P>* encryption_info = NULL;
+ const macho_thread_command<P>* threadInfo = NULL;
const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
const uint32_t cmd_count = fHeader->ncmds();
switch ( cmd->cmd() ) {
case macho_segment_command<P>::CMD:
case LC_SYMTAB:
- case LC_UNIXTHREAD:
case LC_DYSYMTAB:
case LC_LOAD_DYLIB:
case LC_ID_DYLIB:
case LC_VERSION_MIN_MACOSX:
case LC_VERSION_MIN_IPHONEOS:
case LC_FUNCTION_STARTS:
+ case LC_RPATH:
break;
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY:
if ( fHeader->flags() & MH_NO_REEXPORTED_DYLIBS )
throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA";
break;
+ case LC_UNIXTHREAD:
+ if ( fHeader->filetype() != MH_EXECUTE )
+ throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
+ threadInfo = (macho_thread_command<P>*)cmd;
+ break;
default:
throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd());
}
std::vector<std::pair<pint_t, pint_t> > segmentAddressRanges;
std::vector<std::pair<pint_t, pint_t> > segmentFileOffsetRanges;
const macho_segment_command<P>* linkEditSegment = NULL;
+ const macho_segment_command<P>* stackSegment = NULL;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
if ( endOffset > fLength )
throw "segment fileoff+filesize does not fit in file";
- // keep LINKEDIT segment
+ // record special segments
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
linkEditSegment = segCmd;
+ else if ( strcmp(segCmd->segname(), "__UNIXSTACK") == 0 )
+ stackSegment = segCmd;
// cache interesting segments
if ( fFirstSegment == NULL )
fFirstSegment = segCmd;
+ if ( (fTEXTSegment == NULL) && (strcmp(segCmd->segname(), "__TEXT") == 0) )
+ fTEXTSegment = segCmd;
if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) {
if ( fFirstWritableSegment == NULL )
fFirstWritableSegment = segCmd;
if ( segCmd->vmaddr() > 0x100000000ULL )
fWriteableSegmentWithAddrOver4G = true;
}
-
+
// check section ranges
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) {
- // check all sections are within segment
+ // check all non-zero sized sections are within segment
if ( sect->addr() < startAddr )
throwf("section %s vm address not within segment", sect->sectname());
if ( (sect->addr()+sect->size()) > endAddr )
throwf("section %s vm address not within segment", sect->sectname());
if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL)
&& ((sect->flags() & SECTION_TYPE) != S_THREAD_LOCAL_ZEROFILL)
- && (segCmd->filesize() != 0) ) {
+ && (segCmd->filesize() != 0)
+ && (sect->size() != 0) ) {
if ( sect->offset() < startOffset )
throwf("section %s file offset not within segment", sect->sectname());
if ( (sect->offset()+sect->size()) > endOffset )
if ( linkEditSegment == NULL )
throw "no __LINKEDIT segment";
+ // verify there was an executable __TEXT segment and load commands are in it
+ if ( fTEXTSegment == NULL )
+ throw "no __TEXT segment";
+ if ( fTEXTSegment->initprot() != (VM_PROT_READ|VM_PROT_EXECUTE) )
+ throw "__TEXT segment does not have r-x init permissions";
+ //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
+ // throw "__TEXT segment does not have rwx max permissions";
+ if ( fTEXTSegment->fileoff() != 0 )
+ throw "__TEXT segment does not start at mach_header";
+ if ( fTEXTSegment->filesize() < (sizeof(macho_header<P>)+fHeader->sizeofcmds()) )
+ throw "__TEXT segment smaller than load commands";
+
+ // verify if custom stack used, that stack is in __UNIXSTACK segment
+ if ( threadInfo != NULL ) {
+ pint_t initialSP = getInitialStackPointer(threadInfo);
+ if ( initialSP != 0 ) {
+ if ( stackSegment == NULL )
+ throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
+ if ( (initialSP < stackSegment->vmaddr()) || (initialSP > (stackSegment->vmaddr()+stackSegment->vmsize())) )
+ throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
+ }
+ }
+
+ // verify __UNIXSTACK is zero fill
+ if ( stackSegment != NULL ) {
+ if ( (stackSegment->filesize() != 0) || (stackSegment->fileoff() != 0) )
+ throw "__UNIXSTACK is not a zero-fill segment";
+ if ( stackSegment->vmsize() < 4096 )
+ throw "__UNIXSTACK segment is too small";
+ }
+
+ // verify entry point is in __TEXT segment
+ if ( threadInfo != NULL ) {
+ pint_t initialPC = getEntryPoint(threadInfo);
+ if ( (initialPC < fTEXTSegment->vmaddr()) || (initialPC >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) )
+ throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC);
+ }
+
+
// checks for executables
bool isStaticExecutable = false;
if ( fHeader->filetype() == MH_EXECUTE ) {
throwf("section offset should be zero for zero-fill section %s", sect->sectname());
}
+ // check section's segment name matches segment
+// if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
+// throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
+
// more section tests here
}
+
+
+
template <typename A>
void MachOChecker<A>::checkIndirectSymbolTable()
{
}
+
+
template <typename A>
void MachOChecker<A>::checkSymbolTable()
{
}
+template <typename A>
+void MachOChecker<A>::checkInitTerms()
+{
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
+ 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) {
+ // make sure all magic sections that use indirect symbol table fit within it
+ uint32_t count;
+ pint_t* arrayStart;
+ pint_t* arrayEnd;
+ const char* kind = "initializer";
+ switch ( sect->flags() & SECTION_TYPE ) {
+ case S_MOD_TERM_FUNC_POINTERS:
+ kind = "terminator";
+ // fall through
+ case S_MOD_INIT_FUNC_POINTERS:
+ count = sect->size() / sizeof(pint_t);
+ if ( (count*sizeof(pint_t)) != sect->size() )
+ throwf("%s section size is not an even multiple of element size", sect->sectname());
+ if ( (sect->addr() % sizeof(pint_t)) != 0 )
+ throwf("%s section size is not pointer size aligned", sect->sectname());
+ // check each pointer in array points within TEXT
+ arrayStart = (pint_t*)((char*)fHeader + sect->offset());
+ arrayEnd = (pint_t*)((char*)fHeader + sect->offset() + sect->size());
+ for (pint_t* p=arrayStart; p < arrayEnd; ++p) {
+ pint_t pointer = P::getP(*p);
+ if ( (pointer < fTEXTSegment->vmaddr()) || (pointer >= (fTEXTSegment->vmaddr()+fTEXTSegment->vmsize())) )
+ throwf("%s 0x%08llX points outside __TEXT segment", kind, (long long)pointer);
+ }
+ // check each pointer in array will be rebased and not bound
+ if ( fSlidableImage ) {
+ pint_t sectionBeginAddr = sect->addr();
+ pint_t sectionEndddr = sect->addr() + sect->size();
+ for(pint_t addr = sectionBeginAddr; addr < sectionEndddr; addr += sizeof(pint_t)) {
+ if ( addressIsBindingSite(addr) )
+ throwf("%s at 0x%0llX has binding to external symbol", kind, (long long)addr);
+ if ( ! addressIsRebaseSite(addr) )
+ throwf("%s at 0x%0llX is not rebased", kind, (long long)addr);
+ }
+ }
+ break;
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+}
+
+
template <>
ppc::P::uint_t MachOChecker<ppc>::relocBase()
{
}
}
+template <typename A>
+bool MachOChecker<A>::addressIsRebaseSite(pint_t targetAddr)
+{
+ // look at local relocs
+ const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount];
+ for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) {
+ pint_t relocAddress = reloc->r_address() + this->relocBase();
+ if ( relocAddress == targetAddr )
+ return true;
+ }
+ // look rebase info
+ if ( fDyldInfo != NULL ) {
+ const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->rebase_off();
+ const uint8_t* end = &p[fDyldInfo->rebase_size()];
+
+ uint8_t type = 0;
+ uint64_t segOffset = 0;
+ uint32_t count;
+ uint32_t skip;
+ int segIndex;
+ pint_t segStartAddr = 0;
+ pint_t addr;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
+ uint8_t opcode = *p & REBASE_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case REBASE_OPCODE_DONE:
+ done = true;
+ break;
+ case REBASE_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
+ segOffset += immediate*sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+ for (int i=0; i < immediate; ++i) {
+ addr = segStartAddr+segOffset;
+ if ( addr == targetAddr )
+ return true;
+ //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+ count = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ addr = segStartAddr+segOffset;
+ if ( addr == targetAddr )
+ return true;
+ //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += sizeof(pint_t);
+ }
+ break;
+ case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+ addr = segStartAddr+segOffset;
+ if ( addr == targetAddr )
+ return true;
+ //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ addr = segStartAddr+segOffset;
+ if ( addr == targetAddr )
+ return true;
+ //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ throwf("bad rebase opcode %d", *p);
+ }
+ }
+ }
+ return false;
+}
+
+
+template <typename A>
+bool MachOChecker<A>::addressIsBindingSite(pint_t targetAddr)
+{
+ // look at external relocs
+ const macho_relocation_info<P>* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount];
+ for (const macho_relocation_info<P>* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) {
+ pint_t relocAddress = reloc->r_address() + this->relocBase();
+ if ( relocAddress == targetAddr )
+ return true;
+ }
+ // look bind info
+ if ( fDyldInfo != NULL ) {
+ const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->bind_off();
+ const uint8_t* end = &p[fDyldInfo->bind_size()];
+
+ uint8_t type = 0;
+ uint64_t segOffset = 0;
+ uint32_t count;
+ uint32_t skip;
+ uint8_t flags;
+ const char* symbolName = NULL;
+ int libraryOrdinal = 0;
+ int segIndex;
+ int64_t addend = 0;
+ pint_t segStartAddr = 0;
+ pint_t addr;
+ bool done = false;
+ while ( !done && (p < end) ) {
+ uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
+ uint8_t opcode = *p & BIND_OPCODE_MASK;
+ ++p;
+ switch (opcode) {
+ case BIND_OPCODE_DONE:
+ done = true;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
+ libraryOrdinal = immediate;
+ break;
+ case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+ libraryOrdinal = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+ // the special ordinals are negative numbers
+ if ( immediate == 0 )
+ libraryOrdinal = 0;
+ else {
+ int8_t signExtended = BIND_OPCODE_MASK | immediate;
+ libraryOrdinal = signExtended;
+ }
+ break;
+ case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+ symbolName = (char*)p;
+ while (*p != '\0')
+ ++p;
+ ++p;
+ break;
+ case BIND_OPCODE_SET_TYPE_IMM:
+ type = immediate;
+ break;
+ case BIND_OPCODE_SET_ADDEND_SLEB:
+ addend = read_sleb128(p, end);
+ break;
+ case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+ segIndex = immediate;
+ segStartAddr = segStartAddress(segIndex);
+ segOffset = read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_ADD_ADDR_ULEB:
+ segOffset += read_uleb128(p, end);
+ break;
+ case BIND_OPCODE_DO_BIND:
+ if ( (segStartAddr+segOffset) == targetAddr )
+ return true;
+ segOffset += sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+ if ( (segStartAddr+segOffset) == targetAddr )
+ return true;
+ segOffset += read_uleb128(p, end) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+ if ( (segStartAddr+segOffset) == targetAddr )
+ return true;
+ segOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
+ break;
+ case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+ count = read_uleb128(p, end);
+ skip = read_uleb128(p, end);
+ for (uint32_t i=0; i < count; ++i) {
+ if ( (segStartAddr+segOffset) == targetAddr )
+ return true;
+ segOffset += skip + sizeof(pint_t);
+ }
+ break;
+ default:
+ throwf("bad bind opcode %d", *p);
+ }
+ }
+ }
+ return false;
+}
+
+
static void check(const char* path)
{
struct stat stat_buf;
int main(int argc, const char* argv[])
{
- try {
- for(int i=1; i < argc; ++i) {
- const char* arg = argv[i];
- if ( arg[0] == '-' ) {
- if ( strcmp(arg, "-no_content") == 0 ) {
-
- }
- else {
- throwf("unknown option: %s\n", arg);
- }
+ bool progress = false;
+ int result = 0;
+ for(int i=1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if ( arg[0] == '-' ) {
+ if ( strcmp(arg, "-progress") == 0 ) {
+ progress = true;
}
else {
+ throwf("unknown option: %s\n", arg);
+ }
+ }
+ else {
+ bool success = true;
+ try {
check(arg);
}
+ catch (const char* msg) {
+ fprintf(stderr, "machocheck failed: %s %s\n", arg, msg);
+ result = 1;
+ success = false;
+ }
+ if ( success && progress )
+ printf("ok: %s\n", arg);
}
}
- catch (const char* msg) {
- fprintf(stderr, "machocheck failed: %s\n", msg);
- return 1;
- }
- return 0;
+ return result;
}
onlyArchs.insert(CPU_TYPE_I386);
else if ( strcmp(arch, "x86_64") == 0 )
onlyArchs.insert(CPU_TYPE_X86_64);
- else if ( strcmp(arch, "arm") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
- else if ( strcmp(arch, "armv6") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
- else
- throwf("unknown architecture %s", arch);
+ else {
+ bool found = false;
+ for (const ARMSubType* t=ARMSubTypes; t->subTypeName != NULL; ++t) {
+ if ( strcmp(t->subTypeName,arch) == 0 ) {
+ onlyArchs.insert(CPU_TYPE_ARM);
+ found = true;
+ break;
+ }
+ }
+ if ( !found )
+ throwf("unknown architecture %s", arch);
+ }
}
else {
usage();
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
const char* path, bool showFunctionNames);
bool findUnwindSection();
void printUnwindSection(bool showFunctionNames);
+ void printObjectUnwindSection(bool showFunctionNames);
void getSymbolTableInfo();
- const char* functionName(pint_t addr);
+ const char* functionName(pint_t addr, uint32_t* offset=NULL);
+ const char* personalityName(const macho_relocation_info<typename A::P>* reloc);
+ bool hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr=NULL);
+
static const char* archName();
static void decode(uint32_t encoding, const uint8_t* funcStart, char* str);
template <> const char* UnwindPrinter<x86_64>::archName() { return "x86_64"; }
template <> const char* UnwindPrinter<arm>::archName() { return "arm"; }
-template <>
-bool UnwindPrinter<ppc>::validFile(const uint8_t* fileContent)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
- return false;
- if ( header->cputype() != CPU_TYPE_POWERPC )
- return false;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
- return true;
- }
- return false;
-}
-
-template <>
-bool UnwindPrinter<ppc64>::validFile(const uint8_t* fileContent)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC_64 )
- return false;
- if ( header->cputype() != CPU_TYPE_POWERPC64 )
- return false;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
- return true;
- }
- return false;
-}
template <>
bool UnwindPrinter<x86>::validFile(const uint8_t* fileContent)
case MH_DYLIB:
case MH_BUNDLE:
case MH_DYLINKER:
+ case MH_OBJECT:
return true;
}
return false;
case MH_DYLIB:
case MH_BUNDLE:
case MH_DYLINKER:
- return true;
- }
- return false;
-}
-
-template <>
-bool UnwindPrinter<arm>::validFile(const uint8_t* fileContent)
-{
- const macho_header<P>* header = (const macho_header<P>*)fileContent;
- if ( header->magic() != MH_MAGIC )
- return false;
- if ( header->cputype() != CPU_TYPE_ARM )
- return false;
- switch (header->filetype()) {
- case MH_EXECUTE:
- case MH_DYLIB:
- case MH_BUNDLE:
- case MH_DYLINKER:
+ case MH_OBJECT:
return true;
}
return false;
getSymbolTableInfo();
- if ( findUnwindSection() )
- printUnwindSection(showFunctionNames);
+ if ( findUnwindSection() ) {
+ if ( fHeader->filetype() == MH_OBJECT )
+ printObjectUnwindSection(showFunctionNames);
+ else
+ printUnwindSection(showFunctionNames);
+ }
}
}
template <typename A>
-const char* UnwindPrinter<A>::functionName(pint_t addr)
+const char* UnwindPrinter<A>::functionName(pint_t addr, uint32_t* offset)
{
+ const macho_nlist<P>* closestSymbol = NULL;
+ if ( offset != NULL )
+ *offset = 0;
for (uint32_t i=0; i < fSymbolCount; ++i) {
uint8_t type = fSymbols[i].n_type();
if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) {
//fprintf(stderr, "addr=0x%08llX, i=%u, n_type=0x%0X, r=%s\n", (long long)(fSymbols[i].n_value()), i, fSymbols[i].n_type(), r);
return r;
}
+ else if ( offset != NULL ) {
+ if ( closestSymbol == NULL ) {
+ if ( fSymbols[i].n_value() < addr )
+ closestSymbol = &fSymbols[i];
+ }
+ else {
+ if ( (fSymbols[i].n_value() < addr) && (fSymbols[i].n_value() > closestSymbol->n_value()) )
+ closestSymbol = &fSymbols[i];
+ }
+ }
}
}
+ if ( closestSymbol != NULL ) {
+ *offset = addr - closestSymbol->n_value();
+ return &fStrings[closestSymbol->n_strx()];
+ }
return "--anonymous function--";
}
template <typename A>
bool UnwindPrinter<A>::findUnwindSection()
{
+ const char* unwindSectionName = "__unwind_info";
+ const char* unwindSegmentName = "__TEXT";
+ if ( fHeader->filetype() == MH_OBJECT ) {
+ unwindSectionName = "__compact_unwind";
+ unwindSegmentName = "__LD";
+ }
const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
const uint32_t cmd_count = fHeader->ncmds();
const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
- if ( (strcmp(sect->sectname(), "__unwind_info") == 0) && (strcmp(sect->segname(), "__TEXT") == 0) ) {
+ if ( (strncmp(sect->sectname(), unwindSectionName, 16) == 0) && (strcmp(sect->segname(), unwindSegmentName) == 0) ) {
fUnwindSection = sect;
fMachHeaderAddress = segCmd->vmaddr();
return fUnwindSection;
}
+
+template <>
+const char* UnwindPrinter<x86_64>::personalityName(const macho_relocation_info<x86_64::P>* reloc)
+{
+ //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
+ //assert((reloc->r_type() == X86_64_RELOC_UNSIGNED) && "wrong reloc type on personality column in __compact_unwind section");
+ const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
+ return &fStrings[sym.n_strx()];
+}
+
+template <>
+const char* UnwindPrinter<x86>::personalityName(const macho_relocation_info<x86::P>* reloc)
+{
+ //assert(reloc->r_extern() && "reloc not extern on personality column in __compact_unwind section");
+ //assert((reloc->r_type() == GENERIC_RELOC_VANILLA) && "wrong reloc type on personality column in __compact_unwind section");
+ const macho_nlist<P>& sym = fSymbols[reloc->r_symbolnum()];
+ return &fStrings[sym.n_strx()];
+}
+
template <typename A>
-void UnwindPrinter<A>::decode(uint32_t encoding, const uint8_t* funcStart, char* str)
+bool UnwindPrinter<A>::hasExernReloc(uint64_t sectionOffset, const char** personalityStr, pint_t* addr)
{
-
+ const macho_relocation_info<P>* relocs = (macho_relocation_info<P>*)((uint8_t*)fHeader + fUnwindSection->reloff());
+ const macho_relocation_info<P>* relocsEnd = &relocs[fUnwindSection->nreloc()];
+ for (const macho_relocation_info<P>* reloc = relocs; reloc < relocsEnd; ++reloc) {
+ if ( reloc->r_extern() && (reloc->r_address() == sectionOffset) ) {
+ *personalityStr = this->personalityName(reloc);
+ if ( addr != NULL )
+ *addr = fSymbols[reloc->r_symbolnum()].n_value();
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename A>
+void UnwindPrinter<A>::printObjectUnwindSection(bool showFunctionNames)
+{
+ printf("Arch: %s, Section: __LD,__compact_unwind (size=0x%08llX, => %lld entries)\n",
+ archName(), fUnwindSection->size(), fUnwindSection->size() / sizeof(macho_compact_unwind_entry<P>));
+
+ const macho_compact_unwind_entry<P>* const entriesStart = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset());
+ const macho_compact_unwind_entry<P>* const entriesEnd = (macho_compact_unwind_entry<P>*)((uint8_t*)fHeader + fUnwindSection->offset() + fUnwindSection->size());
+ for (const macho_compact_unwind_entry<P>* entry=entriesStart; entry < entriesEnd; ++entry) {
+ uint64_t entryAddress = ((char*)entry - (char*)entriesStart) + fUnwindSection->addr();
+ printf("0x%08llX:\n", entryAddress);
+ const char* functionNameStr;
+ pint_t funcAddress;
+ uint32_t offsetInFunction;
+ if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::codeStartFieldOffset(), &functionNameStr, &funcAddress) ) {
+ offsetInFunction = entry->codeStart();
+ }
+ else {
+ functionNameStr = this->functionName(entry->codeStart(), &offsetInFunction);
+ }
+ if ( offsetInFunction == 0 )
+ printf(" start: 0x%08llX %s\n", (uint64_t)funcAddress, functionNameStr);
+ else
+ printf(" start: 0x%08llX %s+0x%X\n", (uint64_t)funcAddress+offsetInFunction, functionNameStr, offsetInFunction);
+
+ printf(" end: 0x%08llX (len=0x%08X)\n", (uint64_t)(funcAddress+offsetInFunction+entry->codeLen()), entry->codeLen());
+
+ char encodingString[200];
+ this->decode(entry->compactUnwindInfo(), ((const uint8_t*)fHeader), encodingString);
+ printf(" unwind info: 0x%08X %s\n", entry->compactUnwindInfo(), encodingString);
+
+ const char* personalityNameStr;
+ if ( hasExernReloc(((char*)entry-(char*)entriesStart)+macho_compact_unwind_entry<P>::personalityFieldOffset(), &personalityNameStr) ) {
+ printf(" personality: %s\n", personalityNameStr);
+ }
+ else {
+ printf(" personality:\n");
+ }
+ if ( entry->lsda() == 0 ) {
+ printf(" lsda:\n");
+ }
+ else {
+ uint32_t lsdaOffset;
+ const char* lsdaName = this->functionName(entry->lsda(), &lsdaOffset);
+ if ( lsdaOffset == 0 )
+ printf(" lsda: 0x%08llX %s\n", (uint64_t)entry->lsda(), lsdaName);
+ else
+ printf(" lsda: 0x%08llX %s+0x%X\n", (uint64_t)entry->lsda(), lsdaName, lsdaOffset);
+ }
+ }
+
}
+
+
template <typename A>
void UnwindPrinter<A>::printUnwindSection(bool showFunctionNames)
{
unsigned int cputype = OSSwapBigToHostInt32(archs[i].cputype);
if ( onlyArchs.count(cputype) ) {
switch(cputype) {
- case CPU_TYPE_POWERPC:
- if ( UnwindPrinter<ppc>::validFile(p + offset) )
- UnwindPrinter<ppc>::make(p + offset, size, path, showFunctionNames);
- else
- throw "in universal file, ppc slice does not contain ppc mach-o";
- break;
case CPU_TYPE_I386:
if ( UnwindPrinter<x86>::validFile(p + offset) )
UnwindPrinter<x86>::make(p + offset, size, path, showFunctionNames);
else
throw "in universal file, i386 slice does not contain i386 mach-o";
break;
- case CPU_TYPE_POWERPC64:
- if ( UnwindPrinter<ppc64>::validFile(p + offset) )
- UnwindPrinter<ppc64>::make(p + offset, size, path, showFunctionNames);
- else
- throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
- break;
case CPU_TYPE_X86_64:
if ( UnwindPrinter<x86_64>::validFile(p + offset) )
UnwindPrinter<x86_64>::make(p + offset, size, path, showFunctionNames);
else
throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
break;
- case CPU_TYPE_ARM:
- if ( UnwindPrinter<arm>::validFile(p + offset) )
- UnwindPrinter<arm>::make(p + offset, size, path, showFunctionNames);
- else
- throw "in universal file, arm slice does not contain arm mach-o";
- break;
default:
throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
}
else if ( UnwindPrinter<x86>::validFile(p) && onlyArchs.count(CPU_TYPE_I386) ) {
UnwindPrinter<x86>::make(p, length, path, showFunctionNames);
}
- else if ( UnwindPrinter<ppc>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC) ) {
- UnwindPrinter<ppc>::make(p, length, path, showFunctionNames);
- }
- else if ( UnwindPrinter<ppc64>::validFile(p) && onlyArchs.count(CPU_TYPE_POWERPC64) ) {
- UnwindPrinter<ppc64>::make(p, length, path, showFunctionNames);
- }
else if ( UnwindPrinter<x86_64>::validFile(p) && onlyArchs.count(CPU_TYPE_X86_64) ) {
UnwindPrinter<x86_64>::make(p, length, path, showFunctionNames);
}
- else if ( UnwindPrinter<arm>::validFile(p) && onlyArchs.count(CPU_TYPE_ARM) ) {
- UnwindPrinter<arm>::make(p, length, path, showFunctionNames);
- }
else {
throw "not a known file type";
}
if ( arg[0] == '-' ) {
if ( strcmp(arg, "-arch") == 0 ) {
const char* arch = argv[++i];
- if ( strcmp(arch, "ppc") == 0 )
- onlyArchs.insert(CPU_TYPE_POWERPC);
- else if ( strcmp(arch, "ppc64") == 0 )
- onlyArchs.insert(CPU_TYPE_POWERPC64);
- else if ( strcmp(arch, "i386") == 0 )
+ if ( strcmp(arch, "i386") == 0 )
onlyArchs.insert(CPU_TYPE_I386);
else if ( strcmp(arch, "x86_64") == 0 )
onlyArchs.insert(CPU_TYPE_X86_64);
- else if ( strcmp(arch, "arm") == 0 )
- onlyArchs.insert(CPU_TYPE_ARM);
else
throwf("unknown architecture %s", arch);
}
// use all architectures if no restrictions specified
if ( onlyArchs.size() == 0 ) {
- onlyArchs.insert(CPU_TYPE_POWERPC);
- onlyArchs.insert(CPU_TYPE_POWERPC64);
onlyArchs.insert(CPU_TYPE_I386);
onlyArchs.insert(CPU_TYPE_X86_64);
- onlyArchs.insert(CPU_TYPE_ARM);
}
// process each file
# set default to be all
VALID_ARCHS ?= "i386 x86_64 armv6"
+IOS_SDK = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.Internal.sdk
+
MYDIR=$(shell cd ../../bin;pwd)
LD = ld
OBJECTDUMP = ObjectDump
ifdef BUILT_PRODUCTS_DIR
# if run within Xcode, add the just built tools to the command path
PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${PATH}
- COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${MYDIR}:${COMPILER_PATH}
+ COMPILER_PATH := ${BUILT_PRODUCTS_DIR}:${COMPILER_PATH}
+ LD_PATH = ${BUILT_PRODUCTS_DIR}
LD = ${BUILT_PRODUCTS_DIR}/ld
OBJECTDUMP = ${BUILT_PRODUCTS_DIR}/ObjectDump
MACHOCHECK = ${BUILT_PRODUCTS_DIR}/machocheck
else
ifneq "$(findstring /unit-tests/test-cases/, $(shell pwd))" ""
# if run from Terminal inside unit-test directory
- RELEASEDIR=$(shell cd ../../../build/Release;pwd)
RELEASEADIR=$(shell cd ../../../build/Release-assert;pwd)
DEBUGDIR=$(shell cd ../../../build/Debug;pwd)
PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${PATH}
- COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${MYDIR}:${COMPILER_PATH}
+ COMPILER_PATH := ${RELEASEADIR}:${RELEASEDIR}:${DEBUGDIR}:${COMPILER_PATH}
+ LD_PATH = ${DEBUGDIR}
LD = ${DEBUGDIR}/ld
OBJECTDUMP = ${DEBUGDIR}/ObjectDump
MACHOCHECK = ${DEBUGDIR}/machocheck
endif
export PATH
export COMPILER_PATH
+export GCC_EXEC_PREFIX=garbage
ifeq ($(ARCH),ppc)
SDKExtra = -isysroot /Developer/SDKs/MacOSX10.6.sdk
endif
CC = cc -arch ${ARCH} ${SDKExtra}
-CCFLAGS = -Wall -std=c99
+CCFLAGS = -Wall
ASMFLAGS =
+VERSION_NEW_LINKEDIT = -mmacosx-version-min=10.6
+VERSION_OLD_LINKEDIT = -mmacosx-version-min=10.4
+LD_NEW_LINKEDIT = -macosx_version_min 10.6
CXX = c++ -arch ${ARCH} ${SDKExtra}
CXXFLAGS = -Wall
ifeq ($(ARCH),armv6)
- SDKExtra = -isysroot /Developer/SDKs/Extra
- LDFLAGS := -syslibroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot $(IOS_SDK)
override FILEARCH = arm
+ CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
+ VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
+ LD_SYSROOT = -syslibroot $(IOS_SDK)
+ LD_NEW_LINKEDIT = -ios_version_min 4.0
else
FILEARCH = $(ARCH)
endif
ifeq ($(ARCH),armv7)
- SDKExtra = -isysroot /Developer/SDKs/Extra
- LDFLAGS := -syslibroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot $(IOS_SDK)
override FILEARCH = arm
+ CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
+ VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
+ LD_SYSROOT = -syslibroot $(IOS_SDK)
+ LD_NEW_LINKEDIT = -ios_version_min 4.0
else
FILEARCH = $(ARCH)
endif
ifeq ($(ARCH),thumb)
- SDKExtra = -isysroot /Developer/SDKs/Extra
- LDFLAGS := -syslibroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot $(IOS_SDK)
CCFLAGS += -mthumb
CXXFLAGS += -mthumb
override ARCH = armv6
override FILEARCH = arm
+ CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
+ VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
+ LD_SYSROOT = -syslibroot $(IOS_SDK)
+ LD_NEW_LINKEDIT = -ios_version_min 4.0
else
FILEARCH = $(ARCH)
endif
ifeq ($(ARCH),thumb2)
- SDKExtra = -isysroot /Developer/SDKs/Extra
- LDFLAGS := -syslibroot /Developer/SDKs/Extra
+ LDFLAGS := -syslibroot $(IOS_SDK)
CCFLAGS += -mthumb
CXXFLAGS += -mthumb
override ARCH = armv7
override FILEARCH = arm
- CC = /Volumes/Leopard/Developer/Platforms/iPhoneOS.platform/usr/bin/gcc-4.2 -arch ${ARCH}
+ CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang++ -arch ${ARCH} -ccc-install-dir ${LD_PATH} -miphoneos-version-min=5.0 -isysroot $(IOS_SDK)
+ VERSION_NEW_LINKEDIT = -miphoneos-version-min=4.0
+ VERSION_OLD_LINKEDIT = -miphoneos-version-min=3.0
+ LD_SYSROOT = -syslibroot $(IOS_SDK)
+ LD_NEW_LINKEDIT = -ios_version_min 4.0
else
FILEARCH = $(ARCH)
endif
[ "$PROCTORRUN" ] && exec ../proctor-run
-all_archs="x86_64 ppc i386 "
-#armv6 thumb
-valid_archs="x86_64 armv6 ppc i386 "
+all_archs="x86_64 i386"
+valid_archs="x86_64 i386"
+# only test arm code if iOS platform tools installed
+if [ -d /Developer/Platforms/iPhoneOS.platform/Developer/SDKs ]
+then
+ all_archs="${all_archs} armv7"
+ valid_archs="${valid_archs} armv7"
+fi
+
# clean first
../bin/make-recursive.pl clean > /dev/null
all:
${CC} ${CCFLAGS} main.c -static -c
${CC} ${CCFLAGS} abs.s -static -c
- ${LD} -arch ${ARCH} -static main.o abs.o -e _main -o main -new_linker
- ${LD} -arch ${ARCH} -static -r main.o abs.o -o all.o -new_linker
+ ${LD} -arch ${ARCH} -static main.o abs.o -e _main -o main -new_linker -macosx_version_min 10.6
+ ${LD} -arch ${ARCH} -static -r main.o abs.o -o all.o -new_linker
nm -m all.o | grep _myAbs | grep absolute | ${FAIL_IF_EMPTY}
- ${LD} -arch ${ARCH} -static all.o -e _main -o main2 -new_linker
+ ${LD} -arch ${ARCH} -static all.o -e _main -o main2 -new_linker -macosx_version_min 10.6
${PASS_IFF_GOOD_MACHO} main2
clean:
.globl _myAbs
#if __LP64__
- _myAbs = 0x012345678
+ _myAbs = 0x102345678
#else
_myAbs = 0xfe000000
#endif
*/
.text
-
+ .align 4
+
_temp: nop
nop
*/
.text
-
+ .align 4
+
_temp: nop
nop
ifeq ($(ARCH),x86_64)
IMAGE_INFO = "__objc_imageinfo"
endif
-ifeq ($(ARCH),armv6)
+ifeq (${FILEARCH},arm)
IMAGE_INFO = "__objc_imageinfo"
endif
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Check the order of functions from archives.
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -o foo.o
+ ${CC} ${CCFLAGS} foo2.c -c -o foo2.o
+ ${CC} ${CCFLAGS} foo3.c -c -o foo3.o
+ ${CC} ${CCFLAGS} bar.c -c -o bar.o
+ ${CC} ${CCFLAGS} bar2.c -c -o bar2.o
+ ${CC} ${CCFLAGS} bar3.c -c -o bar3.o
+ libtool -static foo.o foo2.o foo3.o -o libfoo.a
+ libtool -static bar3.o bar2.o bar.o -o libbar.a
+ ${CC} ${CCFLAGS} main.c -lbar -lfoo -L. -o main
+ nm -njg main | egrep 'foo|bar' > found.order
+ diff found.order expected.order
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf main *.o *.a found.order
--- /dev/null
+int bar() { return 0; }
--- /dev/null
+int bar2() { return 0; }
--- /dev/null
+int bar3() { return 0; }
--- /dev/null
+_bar3
+_bar2
+_bar
+_foo
+_foo2
+_foo3
--- /dev/null
+int foo() { return 1; }
--- /dev/null
+int foo2() { return 1; }
--- /dev/null
+int foo3() { return 1; }
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 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@
+ */
+
+extern int foo();
+extern int foo2();
+extern int foo3();
+extern int bar();
+extern int bar2();
+extern int bar3();
+
+int main()
+{
+ foo();
+ bar();
+ foo2();
+ bar2();
+ foo3();
+ bar3();
+ return 0;
+}
\ No newline at end of file
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
+ifeq ($(FILEARCH),arm)
+ LD_VERS = -ios_version_min 4.0 -syslibroot $(IOS_SDK)
+else
+ LD_VERS = -macosx_version_min 10.6
+endif
+
#
# Check that ld can figure out architecture from .o files without -arch option
all:
${CC} ${CCFLAGS} hello.c -c -o hello.o
${FAIL_IF_BAD_OBJ} hello.o
- ${LD} ${LDFLAGS} -lcrt1.o hello.o -o hello -lSystem
- file hello | grep ${FILEARCH} | ${PASS_IFF_STDIN}
+ ${LD} ${LDFLAGS} hello.o -dylib -o hello.dylib -lSystem $(LD_VERS)
+ file hello.dylib | grep ${FILEARCH} | ${PASS_IFF_STDIN}
clean:
- rm -rf hello.o hello
+ rm -rf hello.o hello.dylib
int main()
{
fprintf(stdout, "hello\n");
+ return 0;
}
${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib
${CC} ${CCFLAGS} main.c libfoo.dylib libbar.dylib -o main -Wl,-bind_at_load
- dyldinfo -lazy_bind main | grep "no compressed lazy binding info" | ${FAIL_IF_EMPTY}
- dyldinfo -bind main | grep _bar | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -lazy_bind main | grep "no compressed lazy binding info" | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -bind main | grep _bar | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
clean:
.text
+ .align 4
+
#if __thumb__
.thumb_func _bar
.code 16
.text
+ .align 4
#if __thumb__
.thumb_func _foo
blx _bar
// b _bar
-
+ .align 4
_space1:
#if __thumb2__
all:
${CC} ${CCFLAGS} mythumb.s -c
${CC} ${CCFLAGS} myarm.s -c
+ ${LD} -arch ${ARCH} mythumb.o myarm.o -r -o all.o
${CC} ${CCFLAGS} mythumb.o myarm.o -dynamiclib -o liball.dylib
${PASS_IFF_GOOD_MACHO} liball.dylib
.text
+ .align 4
#if __arm__
.code 32
.align 2
.text
+ .align 4
+_junk:
+ nop
+ nop
+ nop
+ nop
#if __arm__
.syntax unified
.thumb_func _mythumb
{
fprintf(stdout, "hello\n");
foo();
+ return 0;
}
--- /dev/null
+ .long 0x12340000
+
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+SHELL = bash # use bash shell so we can redirect just stderr
+
+#
+# Verify machochecker catchs init routines to abolute addresses outside image
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c init.s -o init.exe
+ ${FAIL_IF_SUCCESS} ${MACHOCHECK} init.exe 2>/dev/null
+ ${CC} ${CCFLAGS} main.c term.s -o term.exe
+ ${FAIL_IF_SUCCESS} ${MACHOCHECK} term.exe 2>/dev/null
+ ${PASS_IFF} /usr/bin/true
+
+clean:
+ rm -f init.exe term.exe
--- /dev/null
+
+
+ .mod_init_func
+#if __LP64__
+ .quad 0x7FFF123400000000
+#else
+ .long 0x12340000
+#endif
+
+
--- /dev/null
+int main() { return 0; }
+
--- /dev/null
+
+
+ .mod_term_func
+#if __LP64__
+ .quad 0x7FFF123400000000
+#else
+ .long 0x12340000
+#endif
+
+
--- /dev/null
+ .long 0x12340000
+
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+SHELL = bash # use bash shell so we can redirect just stderr
+
+#
+# Verify machochecker catchs init routines with bindings to external symbols
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c init.s -Wl,-pie -o init.exe
+ ${FAIL_IF_SUCCESS} ${MACHOCHECK} init.exe 2>/dev/null
+ ${CC} ${CCFLAGS} main.c term.s -Wl,-pie -o term.exe
+ ${FAIL_IF_SUCCESS} ${MACHOCHECK} term.exe 2>/dev/null
+ ${PASS_IFF} /usr/bin/true
+
+clean:
+ rm -f init.exe term.exe
--- /dev/null
+
+
+ .mod_init_func
+#if __LP64__
+ .quad _malloc + 0x100000010
+#else
+ .long _malloc + 0x1010
+#endif
+
+
--- /dev/null
+int main() { return 0; }
+
--- /dev/null
+
+
+ .mod_term_func
+#if __LP64__
+ .quad _malloc + 0x100000010
+#else
+ .long _malloc + 0x1010
+#endif
+
+
--- /dev/null
+ .long 0x12340000
+
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+SHELL = bash # use bash shell so we can redirect just stderr
+
+#
+# Verify machochecker catchs init routines in slidable images that are missing rebasing info
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c init.s -Wl,-pie -o init.exe
+ ${FAIL_IF_SUCCESS} ${MACHOCHECK} init.exe 2>/dev/null
+ ${CC} ${CCFLAGS} main.c term.s -Wl,-pie -o term.exe
+ ${FAIL_IF_SUCCESS} ${MACHOCHECK} term.exe 2>/dev/null
+ ${PASS_IFF} /usr/bin/true
+
+clean:
+ rm -f init.exe term.exe
--- /dev/null
+
+
+ .mod_init_func
+#if __LP64__
+ .quad 0x100000010
+#else
+ .long 0x1010
+#endif
+
+
--- /dev/null
+int main() { return 0; }
+
--- /dev/null
+
+
+ .mod_term_func
+#if __LP64__
+ .quad 0x100000010
+#else
+ .long 0x1010
+#endif
+
+
# Test the if all symbols from a dylib are weak_import, that the whole dylib is weakly loaded
#
-run: all-${ARCH}
+run: all-${FILEARCH}
all-ppc:
${PASS_IFF} true
-all-armv6:
+all-arm:
${PASS_IFF} true
all-i386: all-real
${FAIL_IF_BAD_MACHO} libfoo.dylib
${CC} ${CCFLAGS} main.c -o main libfoo.dylib
- dyldinfo -bind main | grep wfoo | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -bind main | grep wfoo | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
all:
${CC} ${CCFLAGS} foo.s -c -o foo.o
- nm -m foo.o | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY}
+ nm -m foo.o | grep _mycomm64aligned | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY}
+ nm -m foo.o | grep _mycomm16kaligned | grep '(alignment 2^14)' | ${FAIL_IF_EMPTY}
${LD} foo.o -r -o foo2.o
- nm -m foo2.o | grep '(alignment 2^6)' | ${PASS_IFF_STDIN}
+ nm -m foo2.o | grep _mycomm64aligned | grep '(alignment 2^6)' | ${FAIL_IF_EMPTY}
+ nm -m foo2.o | grep _mycomm16kaligned | grep '(alignment 2^14)' | ${FAIL_IF_EMPTY}
+ ${PASS_IFF} true
clean:
rm -rf foo.o foo2.o
.comm _mycomm64aligned,15,6
+ .comm _mycomm16kaligned,256,14
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Check compiler emitted compact unwind info
+#
+
+all: all-${FILEARCH}
+
+all-i386: all-cu
+all-x86_64: all-cu
+all-arm: all-good
+
+all-cu:
+ ${CC} ${CCFLAGS} test.s -c -o test.o
+ ${OBJECTDUMP} test.o > test.o.dump
+ ${LD} -r -arch ${ARCH} test.o -o test-r.o
+ ${OBJECTDUMP} test-r.o > test-r.o.dump
+ ${PASS_IFF} diff test.o.dump test-r.o.dump
+
+all-good:
+ ${PASS_IFF} true
+
+clean:
+ rm -f test.o test-r.o test.o.dump test-r.o.dump
--- /dev/null
+#if __LP64__
+ #define pointer quad
+#else
+ #define pointer long
+#endif
+
+
+
+ .text
+
+_basic:
+ nop
+ nop
+Lbasicend:
+
+
+_multi:
+ nop
+ nop
+Lmulti1:
+ nop
+Lmulti1a:
+ nop
+ nop
+Lmulti2:
+ nop
+Lmultiend:
+
+
+_person:
+ nop
+ nop
+Lpersonend:
+
+
+_person_lsda:
+ nop
+ nop
+Lpersonlsdaend:
+
+
+ .section __TEXT,__gcc_except_tab
+_lsda1:
+ .long 1
+ .long 2
+
+
+ .section __LD,__compact_unwind,regular,debug
+
+ .pointer _basic
+ .set L1,Lbasicend-_basic
+ .long L1
+ .long 0
+ .pointer 0
+ .pointer 0
+
+ .pointer _multi
+ .set L2,Lmulti1-_multi
+ .long L2
+ .long 1
+ .pointer 0
+ .pointer 0
+
+ .pointer Lmulti1
+ .set L3,Lmulti2-Lmulti1
+ .long L3
+ .long 2
+ .pointer 0
+ .pointer 0
+
+ .pointer Lmulti2
+ .set L4,Lmultiend-Lmulti2
+ .long L4
+ .long 3
+ .pointer 0
+ .pointer 0
+
+
+ .pointer _person
+ .set L5,Lpersonend-_person
+ .long L5
+ .long 0
+ .pointer _gxx_personality_v0
+ .pointer 0
+
+
+ .pointer _person_lsda
+ .set L6,Lpersonlsdaend-_person_lsda
+ .long L6
+ .long 0
+ .pointer _gxx_personality_v0
+ .pointer _lsda1
+
+
+ .subsections_via_symbols
+
+
+
+
test-x86_64:
${PASS_IFF} true
-test-armv6:
- gcc foo.c -arch armv4t -c -DAAA=1 -o foo4.o
- gcc foo.c -arch armv5 -c -DBBB=1 -o foo5.o
- gcc foo.c -arch armv6 -c -DCCC=1 -o foo6.o
- gcc foo.c -arch xscale -c -DDDD=1 -o foox.o
- lipo foo4.o foo5.o foo6.o foox.o -create -output foo.o
+test-armv6: test-arm
+test-armv7: test-arm
+
+test-arm:
+ clang foo.c -arch armv4t -c -DAAA=1 -o foo4.o
+ clang foo.c -arch armv5 -c -DBBB=1 -o foo5.o
+ clang foo.c -arch armv6 -c -DCCC=1 -o foo6.o
+ clang foo.c -arch armv7 -c -DDDD=1 -o foo7.o
+ lipo foo4.o foo5.o foo6.o foo7.o -create -output foo.o
# check -arch armv4t pulls out V4 slice
${LD} -r -arch armv4t foo.o -o fooa.o
otool -hv fooc.o | grep V6 | ${FAIL_IF_EMPTY}
nm fooc.o | grep _ccc | ${FAIL_IF_EMPTY}
- # check -arch xscale pulls out xscale slice
- ${LD} -r -arch xscale foo.o -o fooxx.o
- otool -hv fooxx.o | grep XSCALE | ${FAIL_IF_EMPTY}
- nm fooxx.o | grep _ddd | ${FAIL_IF_EMPTY}
+ # check -arch armv7 pulls out V7 slice
+ ${LD} -r -arch armv7 foo.o -o fooc.o
+ otool -hv fooc.o | grep V7 | ${FAIL_IF_EMPTY}
+ nm fooc.o | grep _ddd | ${FAIL_IF_EMPTY}
${PASS_IFF} true
test-x86_64:
${PASS_IFF} true
-test-armv6:
- gcc foo.c -arch armv4t -c -o foo-v4.o
+test-armv6: test-arm
+test-armv7: test-arm
+
+test-arm:
+ clang foo.c -arch armv4t -c -o foo-v4.o
${FAIL_IF_BAD_OBJ} foo-v4.o
- gcc foo.c -arch armv5 -c -o foo-v5.o
+ clang foo.c -arch armv5 -c -o foo-v5.o
${FAIL_IF_BAD_OBJ} foo-v5.o
- gcc foo.c -arch armv6 -c -o foo-v6.o
+ clang foo.c -arch armv6 -c -o foo-v6.o
${FAIL_IF_BAD_OBJ} foo-v6.o
- gcc foo.c -arch xscale -c -o foo-xscale.o
+ clang foo.c -arch armv7 -c -o foo-v7.o
+ ${FAIL_IF_BAD_OBJ} foo-v7.o
+ clang foo.c -arch xscale -c -o foo-xscale.o
${FAIL_IF_BAD_OBJ} foo-xscale.o
- gcc main.c -arch armv4t -c -o main-v4.o
+ clang main.c -arch armv4t -c -o main-v4.o
${FAIL_IF_BAD_OBJ} main-v4.o
- gcc main.c -arch armv5 -c -o main-v5.o
+ clang main.c -arch armv5 -c -o main-v5.o
${FAIL_IF_BAD_OBJ} main-v5.o
- gcc main.c -arch armv6 -c -o main-v6.o
+ clang main.c -arch armv6 -c -o main-v6.o
${FAIL_IF_BAD_OBJ} main-v6.o
- gcc main.c -arch xscale -c -o main-xscale.o
+ clang main.c -arch xscale -c -o main-xscale.o
${FAIL_IF_BAD_OBJ} main-xscale.o
+ clang main.c -arch armv7 -c -o main-v7.o
+ ${FAIL_IF_BAD_OBJ} main-v7.o
# check V4+V4 -> V4
${LD} -r main-v4.o foo-v4.o -o all.o
otool -hv all.o | grep V4T | ${FAIL_IF_EMPTY}
# check V4+V5 -> V5
- ${LD} -r main-v4.o foo-v5.o -o all.o
- ${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY}
+ #${LD} -r main-v4.o foo-v5.o -o all.o
+ #${FAIL_IF_BAD_OBJ} all.o
+ #otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY}
# check V4+V6 -> V6
- ${LD} -r main-v4.o foo-v6.o -o all.o
- ${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY}
+ #${LD} -r main-v4.o foo-v6.o -o all.o
+ #${FAIL_IF_BAD_OBJ} all.o
+ #otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY}
# check V4+xscale -> xscale
- ${LD} -r main-v4.o foo-xscale.o -o all.o
- ${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY}
+ #${LD} -r main-v4.o foo-xscale.o -o all.o
+ #${FAIL_IF_BAD_OBJ} all.o
+ #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY}
# check V5+V5 -> V5
${LD} -r main-v5.o foo-v5.o -o all.o
otool -hv all.o | grep V5 | ${FAIL_IF_EMPTY}
# check V5+V6 -> V6
- ${LD} -r main-v5.o foo-v6.o -o all.o
- ${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY}
+ #${LD} -r main-v5.o foo-v6.o -o all.o
+ #${FAIL_IF_BAD_OBJ} all.o
+ #otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY}
# check V5+xscale -> xscale
- ${LD} -r main-v5.o foo-xscale.o -o all.o
- ${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY}
+ #${LD} -r main-v5.o foo-xscale.o -o all.o
+ #${FAIL_IF_BAD_OBJ} all.o
+ #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY}
# check V6+V6 -> V6
${LD} -r main-v6.o foo-v6.o -o all.o
${FAIL_IF_BAD_OBJ} all.o
otool -hv all.o | grep V6 | ${FAIL_IF_EMPTY}
- # check xscale+xscale -> xscale
- ${LD} -r main-xscale.o foo-xscale.o -o all.o
+ # check V7+V7 -> V7
+ ${LD} -r main-v7.o foo-v7.o -o all.o
${FAIL_IF_BAD_OBJ} all.o
- otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY}
+ otool -hv all.o | grep V7 | ${FAIL_IF_EMPTY}
+
+ # check xscale+xscale -> xscale
+ #${LD} -r main-xscale.o foo-xscale.o -o all.o
+ #${FAIL_IF_BAD_OBJ} all.o
+ #otool -hv all.o | grep XSCALE | ${FAIL_IF_EMPTY}
${PASS_IFF} true
# makes them weak/hidden. Test to make sure that weak/hidden does not leak out.
#
+STRING_LABEL_COUNT = 0
+
ifeq (${ARCH},x86_64)
STRING_LABEL_COUNT = 3
-else
+endif
+ifeq (${ARCH},i386)
STRING_LABEL_COUNT = 1
endif
nm -nm foobar.o | grep __cstring | grep "weak private external" | ${FAIL_IF_STDIN}
nm -m foobar.o | grep __cstring | wc -l | grep ${STRING_LABEL_COUNT} | ${FAIL_IF_EMPTY}
${CC} ${CCFLAGS} foo.o bar.o -dynamiclib -o libfoobar.dylib
- nm -m libfoobar.dylib | grep __cstring | wc -l | grep 1 | ${FAIL_IF_EMPTY}
+ nm -m libfoobar.dylib | grep __cstring | wc -l | egrep '[01]' | ${FAIL_IF_EMPTY}
${PASS_IFF} /usr/bin/true
clean:
#
# Check that a large page zero works
#
+ifeq (,${findstring "macosx","$(VERSION_NEW_LINKEDIT)"})
+ SEG_ADDR = 0x20000000
+else
+ SEG_ADDR = 0xb8000000
+endif
run: all
all:
- ${CC} ${CCFLAGS} main.c zero.s -o main -pagezero_size 0 -segaddr __TEXT 0xb8000000 -segaddr __MYZEROPAGE 0
+ ${CC} ${CCFLAGS} main.c zero.s -o main -pagezero_size 0 -segaddr __TEXT $(SEG_ADDR) -segaddr __MYZEROPAGE 0
${PASS_IFF_GOOD_MACHO} main
clean:
.section __MYZEROPAGE,_data
_min: .long 0
-
+#if __arm__
+ .zerofill __MYZEROPAGE,__zerofill,_padding,536870910
+#else
.zerofill __MYZEROPAGE,__zerofill,_padding,2147483644
-
+#endif
${CC} ${CCFLAGS} foo.c -c -o foo.o
${FAIL_IF_BAD_OBJ} foo.o
libtool -static foo.o -o libfoo.a
- ${CC} ${CCFLAGS} main.c -mdynamic-no-pic -Os libfoo.a -dead_strip -o main
+ ${CC} ${CCFLAGS} main.c -Os libfoo.a -dead_strip -o main
${PASS_IFF_GOOD_MACHO} main
clean:
##
-# Copyright (c) 2009 Apple Inc. All rights reserved.
+# Copyright (c) 2009-2011 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
#
# <rdar://problem/7043256> ld64 can not find a -e entry point from an archive
+# <rdar://problem/8866673> "start" and other special symbols should start out initially undefined
#
run: all
all:
${CC} ${CCFLAGS} foo.c -c -o foo.o
libtool -static foo.o -o libfoo.a
+ ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} bar.c -Os libfoo.a -o foo -Wl,-e,_foo -nostartfiles
${FAIL_IF_ERROR} ${CC} ${CCFLAGS} bar.c -Os libfoo.a -dead_strip -o foo -Wl,-e,_foo
${PASS_IFF_GOOD_MACHO} foo
all:
${CXX} ${CXXFLAGS} main.cxx -c
- ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o -lSystem -o main 2>main1.fail
+ ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o ${LD_SYSROOT} ${LD_NEW_LINKEDIT} -lSystem -o main 2>main1.fail
grep __ZN3Foo4doitEv main1.fail | ${FAIL_IF_EMPTY}
- ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o -lSystem -demangle -o main 2>main2.fail
+ ${FAIL_IF_SUCCESS} ${LD} -arch ${ARCH} -dylib main.o ${LD_SYSROOT} ${LD_NEW_LINKEDIT} -lSystem -demangle -o main 2>main2.fail
grep 'Foo::doit()' main2.fail | ${PASS_IFF_STDIN}
##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2011 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
rm -rf trace_ars
export LD_TRACE_ARCHIVES=1 && export LD_TRACE_FILE=trace_ars && ${CC} ${CCFLAGS} main.c libfoo.a -o main
grep 'libfoo.a' trace_ars | ${FAIL_IF_EMPTY}
+ export LD_TRACE_ARCHIVES=1 && export LD_TRACE_FILE=trace_ars2 && ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoo.a -o main
+ grep 'libfoo.a' trace_ars2 | ${FAIL_IF_EMPTY}
+ export LD_TRACE_ARCHIVES=1 && export LD_TRACE_FILE=trace_ars3 && ${CC} ${CCFLAGS} main.c libfoo.a -o main -all_load
+ grep 'libfoo.a' trace_ars3 | ${FAIL_IF_EMPTY}
${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
rm -rf trace_dylibs
export LD_TRACE_DYLIBS=1 && export LD_TRACE_FILE=trace_dylibs && ${CC} ${CCFLAGS} main.c libfoo.dylib -o main
clean:
- rm -rf foo.o libfoo.a libfoo.dylib main trace_ars trace_dylibs
+ rm -rf foo.o libfoo.a libfoo.dylib main trace_ars trace_ars2 trace_ars3 trace_dylibs
run: all
all:
- ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o hello.o -mdynamic-no-pic
- ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o other.o -mdynamic-no-pic
- ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o hello
+ ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.cxx -c -o hello.o
+ ${CXX} ${CCXXFLAGS} -gdwarf-2 other.cxx -c -o other.o
+ ${CXX} ${CCXXFLAGS} -gdwarf-2 hello.o other.o -o hello -Wl,-order_file,hello.order
${FAIL_IF_BAD_MACHO} hello
nm -ap hello | ./stabs-filter.pl > hello.stabs
${PASS_IFF} diff hello.stabs expected-stabs
0000 ENSYM
0000 BNSYM
0000 FUN __Z3fooi
-0000 SOL CWD//header.h
+0000 SOL CWD/header.h
0000 FUN
0000 ENSYM
0000 SO
--- /dev/null
+
+# order __data section so even if different compiler lays out data
+# differently, linker lays out the same, so stabs are in same order
+.my_non_standard_name_static
+.my_non_standard_name
+__ZZ3bariE8bar_init
+_init
+_uninit
+__ZZ3bariE10bar_uninit
+__ZL7suninit
+__ZL5sinit
+
+
+
+
+
int uninit;
int init = 1;
-static int custom _asm(".my_non_standard_name") = 1;
+static int custom __asm__(".my_non_standard_name") = 1;
static int suninit;
static int sinit=0;
-static int scustominit _asm(".my_non_standard_name_static") = 1;
+static int scustominit __asm__(".my_non_standard_name_static") = 1;
int bar(int x)
{
bar_init + bar_uninit + foo(x);
}
-extern void disappear() _asm("lbegone");
+extern void disappear() __asm__("lbegone");
void disappear() {}
-extern void foo() _asm(".my_non_standard_function_name");
+extern void foo() __asm__(".my_non_standard_function_name");
void foo() { disappear(); }
int main()
{
fprintf(stdout, "hello\n");
+ return 0;
}
\ No newline at end of file
ifeq ($(ARCH),x86_64)
IMAGE_INFO = "__objc_imageinfo"
endif
-ifeq ($(ARCH),armv6)
+ifeq ($(FILEARCH),arm)
IMAGE_INFO = "__objc_imageinfo"
endif
int main()
{
fprintf(stdout, "hello\n");
+ return 0;
}
\ No newline at end of file
# <rdar://problem/5277857> OpenGL.framework and X11 both have a libGL.dylib which can cause ld to segfault if both are found
#
-run: all
+all: all-${ARCH}
-all:
+all-i386: all-mac
+all-x86_64: all-mac
+all-armv6: all-good
+all-armv7: all-good
+
+
+all-mac:
mkdir -p other
${CC} foo.c -dynamiclib -o other/libfoo.dylib -mmacosx-version-min=10.4
${CC} bar.c -dynamiclib -o libbar.dylib other/libfoo.dylib -sub_library libfoo -mmacosx-version-min=10.4
${CC} main.c libfoo.dylib -o main -L. 2>errmsg || true
grep "cycle" errmsg | ${PASS_IFF_STDIN}
-
+all-good:
+ ${PASS_IFF} true
clean:
libtool -static -o LibTest1.lib LibTest1.obj
${CC} -arch $(ARCH) -o MtocTest1.obj $(LOCAL_CC_FLAGS) -c MtocTest.c
libtool -static -o MtocTest1.lib MtocTest1.obj
- ld -arch $(ARCH) -o MtocTest1.macho -u __ModuleEntryPoint -e __ModuleEntryPoint $(LD_ARG) -preload -segalign 0x20 -pie -seg1addr 0x240 -map MtocTest1.map LibTest1.lib MtocTest1.lib
+ ${LD} -arch $(ARCH) -o MtocTest1.macho -u __ModuleEntryPoint -e __ModuleEntryPoint $(LD_ARG) -preload -segalign 0x20 -pie -seg1addr 0x240 -map MtocTest1.map LibTest1.lib MtocTest1.lib
/usr/local/efi/bin/mtoc -subsystem application -align 0x20 -d MtocTest1.macho MtocTest1.macho MtocTest1.pecoff
otool -rR MtocTest1.macho > otool-reloc.log
otool -l MtocTest1.macho > otool-load.log
\r
void _ModuleEntryPoint(void)\r
{ \r
- __asm__ __volatile__ ("int $3"); \r
+ //__asm__ __volatile__ ("int $3"); \r
\r
LibInit ();\r
gWhyAreUninitializedGlobalsBad =1;\r
# the linker can still process unwind info from .o files
# correctly
-run: all
+ifeq (${FILEARCH},arm)
+ FILE_TYPE = KEXTBUNDLE
+endif
-all:
+
+all: all-${FILEARCH}
+all-i386: all-zce
+all-x86_64: all-zce
+all-arm: all-good
+
+all-zce:
${CXX} ${CCXXFLAGS} main.cxx -g -c -o main1.o -Os
#strip main1.o -u -s keep.exp -o main2.o
${LD} main1.o -r -x -exported_symbols_list keep.exp -o main2.o
${UNWINDDUMP} -arch ${ARCH} -no_symbols main2 > main2.unwind
${PASS_IFF} diff main1.unwind main2.unwind
+all-good:
+ ${PASS_IFF} true
+
clean:
rm -f main1* main2*
int main()
{
fprintf(stdout, "hello\n");
+ return 0;
}
{
foo();
fprintf(stdout, "hello\n");
+ return 0;
}
{
foo();
fprintf(stdout, "hello\n");
+ return 0;
}
# as dylib with aliases
${CC} ${CCFLAGS} main.c -dynamiclib -o libmain3.dylib -Wl,-function_starts -Wl,-alias,_mid,midalias
${DYLDINFO} -function_starts libmain3.dylib | grep _bar | ${FAIL_IF_EMPTY}
+ # as static main executable
+ ${CC} ${CCFLAGS} main.c -static -e _main -o main_static -nostdlib -Wl,-new_linker -Wl,-alias,_myexit,_exit
+ ${DYLDINFO} -function_starts main_static | grep _bar | ${FAIL_IF_STDIN}
+ ${CC} ${CCFLAGS} main.c -static -e _main -o main_static -nostdlib -Wl,-new_linker -Wl,-alias,_myexit,_exit -Wl,-function_starts
+ ${DYLDINFO} -function_starts main_static | grep _bar | ${FAIL_IF_EMPTY}
+
+
${PASS_IFF_GOOD_MACHO} main
clean:
- rm main libmain.dylib libmain2.dylib libmain3.dylib
+ rm main libmain.dylib libmain2.dylib libmain3.dylib main_static
int main() { bar(); return 0; }
+#if __STATIC__
+void myexit() {}
+#endif
+
# Check that ld can remove non-lazy pointers for x86_64
#
-all: all-${ARCH}
+all: all-${FILEARCH}
-all-armv6: all-true
+all-arm: all-true
all-ppc: all-true
int main()
{
fprintf(stdout, "hello\n");
+ return 0;
}
int main()
{
fprintf(stdout, "hello\n");
+ return 0;
}
\ No newline at end of file
all:
${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -install_name /usr/lib/libbar.dylib
- ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -reexport_library libbar.dylib -sub_library libbar
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -sub_library libbar
# verify that main gets bar from libbar
${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L.
nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY}
#
# Sanity check that ld can link a kext
#
-
+FILE_TYPE = OBJECT
ifeq (${ARCH},x86_64)
FILE_TYPE = KEXTBUNDLE
-else
- FILE_TYPE = OBJECT
+endif
+ifeq (${FILEARCH},arm)
+ FILE_TYPE = KEXTBUNDLE
endif
+#CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/clang
run: all
all:
- ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykext.c -o mykext.o
- ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykextinfo.c -o mykextinfo.o
- unset LD_NO_CLASSIC_LINKER_STATIC && ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext -Wl,-w
+ ${CC} ${CCFLAGS} -static -mkernel -c mykext.c -o mykext.o
+ ${CC} ${CCFLAGS} -static -mkernel -c mykextinfo.c -o mykextinfo.o
+ ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext -Wl,-w -Wl,-new_linker
otool -hv mykext | grep ${FILE_TYPE} | ${FAIL_IF_EMPTY}
nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY}
nm -nm mykext | grep '(__DATA,__data) external _my_global' | ${FAIL_IF_EMPTY}
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Sanity check that ld can link a kext
+#
+
+EXPORT_LIST = mykext.exp
+FILE_TYPE = OBJECT
+ifeq (${ARCH},x86_64)
+ FILE_TYPE = KEXTBUNDLE
+endif
+ifeq (${FILEARCH},arm)
+ FILE_TYPE = KEXTBUNDLE
+endif
+ifeq (${ARCH},i386)
+ EXPORT_LIST = mykext-i386.exp
+endif
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} -static -mkernel -c mykext.c -o mykext.o
+ ${CC} ${CCFLAGS} -static -mkernel -c mykextinfo.c -o mykextinfo.o
+ ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -exported_symbols_list $(EXPORT_LIST) -o mykext -Wl,-w -Wl,-new_linker
+ otool -hv mykext | grep ${FILE_TYPE} | ${FAIL_IF_EMPTY}
+ nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY}
+ nm -nm mykext | grep '(undefined) external _foo' | ${FAIL_IF_STDIN}
+ nm -nm mykext | grep '(undefined) external _my_used_external_global' | ${FAIL_IF_EMPTY}
+ otool -rv mykext | grep '_extern_global' | ${FAIL_IF_EMPTY}
+ ${PASS_IFF} true
+
+clean:
+ rm -f mykext.o mykextinfo.o mykext
--- /dev/null
+_kmod_info
--- /dev/null
+#include <mach/mach_types.h>
+
+extern void extern_func();
+
+int my_global = 3;
+extern int extern_global;
+extern int extern_unused_global;
+extern int my_used_external_global;
+
+kern_return_t mykext_start (kmod_info_t * ki, void * d) {
+ ++my_global;
+ ++extern_global;
+ ++my_used_external_global;
+ extern_func();
+ return KERN_SUCCESS;
+}
+
+
+kern_return_t mykext_stop (kmod_info_t * ki, void * d) {
+ --my_global;
+ --extern_global;
+ return KERN_SUCCESS;
+}
+
+void my_dead_code() {
+ ++extern_unused_global;
+}
\ No newline at end of file
--- /dev/null
+_kmod_info
+_foo
+_my_used_external_global
--- /dev/null
+#include <mach/mach_types.h>
+
+extern kern_return_t _start(kmod_info_t *ki, void *data);
+extern kern_return_t _stop(kmod_info_t *ki, void *data);
+__private_extern__ kern_return_t mykext_start(kmod_info_t *ki, void *data);
+__private_extern__ kern_return_t mykext_stop(kmod_info_t *ki, void *data);
+
+KMOD_EXPLICIT_DECL(com.yourcompany.kext.mykext, "1.0.0d1", _start, _stop)
+__private_extern__ kmod_start_func_t *_realmain = mykext_start;
+__private_extern__ kmod_stop_func_t *_antimain = mykext_stop;
+__private_extern__ int _kext_apple_cc = __APPLE_CC__ ;
+
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# <rdar://problem/9452006> assert when wacky section order and label at end of section
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.s -c -o foo.o
+ ${PASS_IFF} ${LD} -r foo.o -arch ${ARCH} -o foo-r.o
+
+clean:
+ rm -f foo.o foo-r.o
--- /dev/null
+ .lcomm _mybss ,4, 2
+
+ .text
+ .align 4
+ .globl _main
+_main:
+#if __x86_64__
+ movl $0, _mybss(%rip)
+#elif __i386__
+ movl $0, _mybss
+#elif __arm__
+ .long _mybss
+#endif
+
+ .section __DATA, _stuff
+ .align 4
+_start_stuff:
+ .long 0x0
+ .long 0x0
+_end_stuff:
+
+
+ .subsections_via_symbols
+
\ No newline at end of file
SHELL = bash # use bash shell so we can redirect just stderr
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
LLVMAR = /usr/local/bin/llvm-ar
#
# MachO : main.c : Ufoo2, Ufoo3
#
#echo "Zero..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a.c -c -o a.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b.c -c -o b.o
- ${LLVMGCC} ${CCFLAGS} main.c -c -o main.o
- ${LLVMGCC} ${CCFLAGS} a.o b.o main.o -o main.exe
+ ${CC} ${CCFLAGS} -flto a.c -c -o a.o
+ ${CC} ${CCFLAGS} -flto b.c -c -o b.o
+ ${CC} ${CCFLAGS} main.c -c -o main.o
+ ${CC} ${CCFLAGS} a.o b.o main.o -o main.exe
${PASS_IFF_GOOD_MACHO} main.exe
one:
# MachO : main1.c : Dfoo4, Ufoo2, Ufoo3
#
#echo "One..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a1.c -c -o a1.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b1.c -c -o b1.o
- ${LLVMGCC} ${CCFLAGS} main1.c -c -o main1.o
- ${LLVMGCC} ${CCFLAGS} a1.o b1.o main1.o -o main1.exe
+ ${CC} ${CCFLAGS} -flto a1.c -c -o a1.o
+ ${CC} ${CCFLAGS} -flto b1.c -c -o b1.o
+ ${CC} ${CCFLAGS} main1.c -c -o main1.o
+ ${CC} ${CCFLAGS} a1.o b1.o main1.o -o main1.exe
${PASS_IFF_GOOD_MACHO} main1.exe
two:
# MachO : main2.c : Ufoo2, Ufoo3
#
#echo "Two..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a2.c -c -o a2.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b2.c -c -o b2.o
- ${LLVMGCC} ${CCFLAGS} main2.c -c -o main2.o
- ${LLVMGCC} ${CCFLAGS} a2.o b2.o main2.o -o main2.exe
+ ${CC} ${CCFLAGS} -flto a2.c -c -o a2.o
+ ${CC} ${CCFLAGS} -flto b2.c -c -o b2.o
+ ${CC} ${CCFLAGS} main2.c -c -o main2.o
+ ${CC} ${CCFLAGS} a2.o b2.o main2.o -o main2.exe
${PASS_IFF_GOOD_MACHO} main2.exe
three:
# MachO : main3.c : Ufoo1, Ufoo2, Ubar
#
#echo "Three..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a3.c -c -o a3.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b3.c -c -o b3.o
- ${LLVMGCC} ${CCFLAGS} main3.c -c -o main3.o
- ${LLVMGCC} ${CCFLAGS} a3.o b3.o main3.o -o main3.exe
+ ${CC} ${CCFLAGS} -flto a3.c -c -o a3.o
+ ${CC} ${CCFLAGS} -flto b3.c -c -o b3.o
+ ${CC} ${CCFLAGS} main3.c -c -o main3.o
+ ${CC} ${CCFLAGS} a3.o b3.o main3.o -o main3.exe
${PASS_IFF_GOOD_MACHO} main3.exe
four:
# MachO : main4.c : Dfoo4, Ufoo2, Ufoo3
#
#echo "Four..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a4.c -c -o a4.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b4.c -c -o b4.o
- ${LLVMGCC} ${CCFLAGS} main4.c -c -o main4.o
- ${LLVMGCC} ${CCFLAGS} a4.o b4.o main4.o -o main4.exe
+ ${CC} ${CCFLAGS} -flto a4.c -c -o a4.o
+ ${CC} ${CCFLAGS} -flto b4.c -c -o b4.o
+ ${CC} ${CCFLAGS} main4.c -c -o main4.o
+ ${CC} ${CCFLAGS} a4.o b4.o main4.o -o main4.exe
${PASS_IFF_GOOD_MACHO} main4.exe
five:
# MachO : main5.c : Dfoo3, Ufoo1
#
#echo "verify that if call to mach-o is optimized away, mach-o func is dead stripped"
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a5.c -c -o a5.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b5.c -c -o b5.o
- ${LLVMGCC} ${CCFLAGS} main5.c -c -o main5.o
- ${LLVMGCC} ${CCFLAGS} a5.o b5.o main5.o -o main5.exe -Wl,-dead_strip
+ ${CC} ${CCFLAGS} -flto a5.c -c -o a5.o
+ ${CC} ${CCFLAGS} -flto b5.c -c -o b5.o
+ ${CC} ${CCFLAGS} main5.c -c -o main5.o
+ ${CC} ${CCFLAGS} a5.o b5.o main5.o -o main5.exe -Wl,-dead_strip
${OTOOL} -tV main5.exe | grep foo3 | ${PASS_IFF_EMPTY}
${PASS_IFF_GOOD_MACHO} main5.exe
# MachO : main6.c : Ufoo1
#
#echo "verify dead stripping of foo2 in main executable"
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a6.c -c -o a6.o
- ${LLVMGCC} ${CCFLAGS} main6.c -c -o main6.o
- ${LLVMGCC} ${CCFLAGS} a6.o main6.o -o main6.exe -Wl,-dead_strip
+ ${CC} ${CCFLAGS} -flto a6.c -c -o a6.o
+ ${CC} ${CCFLAGS} main6.c -c -o main6.o
+ ${CC} ${CCFLAGS} a6.o main6.o -o main6.exe -Wl,-dead_strip
${PASS_IFF_GOOD_MACHO} main6.exe
${OTOOL} -tV main6.exe | grep foo2 | ${PASS_IFF_EMPTY}
# MachO : main7.c : Ufoo1
#
#echo "Seven..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a7.c -c -o a7.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b7.c -c -o b7.o
- ${LLVMGCC} ${CCFLAGS} main7.c -c -o main7.o
- ${LLVMGCC} ${CCFLAGS} a7.o b7.o main7.o -o main7.exe
+ ${CC} ${CCFLAGS} -flto a7.c -c -o a7.o
+ ${CC} ${CCFLAGS} -flto b7.c -c -o b7.o
+ ${CC} ${CCFLAGS} main7.c -c -o main7.o
+ ${CC} ${CCFLAGS} a7.o b7.o main7.o -o main7.exe
${PASS_IFF_GOOD_MACHO} main7.exe
eight:
# MachO : main8.c : Ufoo1
#
#echo "Eight..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a8.c -c -o a8.o
- ${LLVMGCC} ${CCFLAGS} main8.c -c -o main8.o
- ${LLVMGCC} ${CCFLAGS} a8.o main8.o -o main8.exe -Wl,-dead_strip
+ ${CC} ${CCFLAGS} -flto a8.c -c -o a8.o
+ ${CC} ${CCFLAGS} main8.c -c -o main8.o
+ ${CC} ${CCFLAGS} a8.o main8.o -o main8.exe -Wl,-dead_strip
${OTOOL} -tV main8.exe | grep foo2 | ${PASS_IFF_EMPTY}
${OTOOL} -tV main8.exe | grep unnamed_2_1 | ${PASS_IFF_EMPTY}
# MachO : main9.c : Ufoo1, Dfoo4
#
#echo "Nine..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a9.c -c -o a9.o
- ${LLVMGCC} ${CCFLAGS} main9.c -c -o main9.o
- ${LLVMGCC} ${CCFLAGS} a9.o main9.o -o main9.exe -Wl,-dead_strip
+ ${CC} ${CCFLAGS} -flto a9.c -c -o a9.o
+ ${CC} ${CCFLAGS} main9.c -c -o main9.o
+ ${CC} ${CCFLAGS} a9.o main9.o -o main9.exe -Wl,-dead_strip
${OTOOL} -tV main9.exe | grep foo2 | ${PASS_IFF_EMPTY}
${OTOOL} -tV main9.exe | grep foo4 | ${PASS_IFF_EMPTY}
${OTOOL} -tV main9.exe | grep unnamed_2_1 | ${PASS_IFF_EMPTY}
# MachO : main10.c
#
#echo "Ten..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a10.c -c -o a10.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b10.c -c -o b10.o
- ${LLVMGCC} ${CCFLAGS} main10.c -c -o main10.o
- ${LLVMGCC} ${CCFLAGS} a10.o b10.o main10.o -o main10.exe
+ ${CC} ${CCFLAGS} -flto a10.c -c -o a10.o
+ ${CC} ${CCFLAGS} -flto b10.c -c -o b10.o
+ ${CC} ${CCFLAGS} main10.c -c -o main10.o
+ ${CC} ${CCFLAGS} a10.o b10.o main10.o -o main10.exe
${PASS_IFF_GOOD_MACHO} main10.exe
eleven:
# MachO : main11.c
#
#echo "Eleven..."
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a11.c -c -o a11.o
- ${LLVMGCC} ${CCFLAGS} main11.c -c -o main11.o
- ${LLVMGCC} ${CCFLAGS} a11.o main11.o -o main11.exe
+ ${CC} ${CCFLAGS} -flto a11.c -c -o a11.o
+ ${CC} ${CCFLAGS} main11.c -c -o main11.o
+ ${CC} ${CCFLAGS} a11.o main11.o -o main11.exe
${PASS_IFF_GOOD_MACHO} main11.exe
twelve:
# MachO : main12.c
#
#echo "verify tentative def in llvm .o referenced from mach-o"
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a12.c -c -o a12.o
- ${LLVMGCC} ${CCFLAGS} main12.c -c -o main12.o
- ${LLVMGCC} ${CCFLAGS} a12.o main12.o -o main12.exe
+ ${CC} ${CCFLAGS} -flto a12.c -c -o a12.o
+ ${CC} ${CCFLAGS} main12.c -c -o main12.o
+ ${CC} ${CCFLAGS} a12.o main12.o -o main12.exe
${PASS_IFF_GOOD_MACHO} main12.exe
thirteen:
# MachO : main13.cc
#
# echo "Thirteen..."
- ${LLVMGCC} ${CXXFLAGS} --emit-llvm a13.cc -c -o a13.o
- ${LLVMGCC} ${CXXFLAGS} main13.cc -c -o main13.o
- ${LLVMGXX} a13.o main13.o -o main13.exe
+ ${CC} ${CXXFLAGS} -flto a13.cc -c -o a13.o
+ ${CC} ${CXXFLAGS} main13.cc -c -o main13.o
+ ${CXX} a13.o main13.o -o main13.exe
fourteen:
#
# llvm : a14.c b14.c
#
# echo "verify an used hidden symbol is removed from a dylib"
- ${LLVMGCC} ${CXXFLAGS} -O4 -dynamiclib a14.c b14.c -o ab14.dylib
+ ${CC} ${CXXFLAGS} -O4 -dynamiclib a14.c b14.c -o ab14.dylib
${FAIL_IF_BAD_MACHO} ab14.dylib
nm -m ab14.dylib | grep _X | ${PASS_IFF_EMPTY}
fifteen:
# echo "verify -dead_strip works with hidden symbols"
- ${LLVMGCC} ${CXXFLAGS} -O4 -Wl,-dead_strip a15.c c15.c -o main15.exe
- ${LLVMGCC} ${CXXFLAGS} -O4 a15.c c15.c -o main15.exe
+ ${CC} ${CXXFLAGS} -O4 -Wl,-dead_strip a15.c c15.c -o main15.exe
+ ${CC} ${CXXFLAGS} -O4 a15.c c15.c -o main15.exe
${FAIL_IF_BAD_MACHO} main15.exe
- ${LLVMGCC} ${CXXFLAGS} -O4 -Wl,-dead_strip -dynamiclib a15.c b15.c -o a15.dylib
- ${LLVMGCC} ${CXXFLAGS} -O4 a15.c b15.c -dynamiclib -o a15.dylib
+ ${CC} ${CXXFLAGS} -O4 -Wl,-dead_strip -dynamiclib a15.c b15.c -o a15.dylib
+ ${CC} ${CXXFLAGS} -O4 a15.c b15.c -dynamiclib -o a15.dylib
${FAIL_IF_BAD_MACHO} a15.dylib
sixteen:
# echo "verify -save-temps"
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main16.c -c -o main16.o
- ${LLVMGCC} ${CCFLAGS} main16.o -o main16.exe -Wl,-save-temps
+ ${CC} ${CCFLAGS} -flto main16.c -c -o main16.o
+ ${CC} ${CCFLAGS} main16.o -o main16.exe -Wl,-save-temps
${PASS_IFF} test -e main16.exe.lto.bc
${PASS_IFF} test -e main16.exe.lto.o
seventeen:
# echo "verify ld -r of all bitcode files produces a bitcode file"
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a17.c -c -o a17.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm b17.c -c -o b17.o
+ ${CC} ${CCFLAGS} -flto a17.c -c -o a17.o
+ ${CC} ${CCFLAGS} -flto b17.c -c -o b17.o
${LD} -arch ${ARCH} -r a17.o b17.o -o ab17.o
file ab17.o | grep "Mach-O" | ${PASS_IFF_EMPTY}
# echo "verify ld -r of bitcode and mach-o produces mach-o"
- ${LLVMGCC} ${CCFLAGS} b17.c -c -o b17m.o
+ ${CC} ${CCFLAGS} b17.c -c -o b17m.o
${LD} -arch ${ARCH} -r a17.o b17m.o -o ab17m.o
file ab17m.o | grep "Mach-O" | ${PASS_IFF_STDIN}
eighteen:
#echo verify ld -r -keep_private_externs works
- ${LLVMGCC} ${CCFLAGS} --emit-llvm a18.c -c -o a18.o
+ ${CC} ${CCFLAGS} -flto a18.c -c -o a18.o
${LD} -arch ${ARCH} -r -keep_private_externs a18.o -o a18-rkpe.o
nm -nm a18-rkpe.o | grep _common_hidden1 | grep "private external" | ${FAIL_IF_EMPTY}
nm -nm a18-rkpe.o | grep _func_hidden2 | grep "private external" | ${FAIL_IF_EMPTY}
nineteen:
#echo verify missing symbol error
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main19.c -c -o main19.o
- ${FAIL_IF_SUCCESS} ${LLVMGCC} ${CCFLAGS} main19.o -o main19.exe 2>fail.log
+ ${CC} ${CCFLAGS} -flto main19.c -c -o main19.o
+ ${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} main19.o -o main19.exe 2>fail.log
grep _foo fail.log | ${PASS_IFF_STDIN}
twenty:
#echo verify bitcode files in archives works
- #${LLVMGCC} ${CCFLAGS} --emit-llvm a20.c -c -o a20.o
- #${LLVMGCC} ${CCFLAGS} --emit-llvm b20.c -c -o b20.o
+ #${CC} ${CCFLAGS} -flto a20.c -c -o a20.o
+ #${CC} ${CCFLAGS} -flto b20.c -c -o b20.o
#libtool -static a20.o b20.o -o lib20.a
- #${LLVMGCC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe
+ #${CC} ${CCFLAGS} main20.c lib20.a -all_load -o main20.exe
#nm main20.exe | grep _foo | ${PASS_IFF_STDIN}
clean:
- rm -rf *.o main*.exe big.* *.dylib main16.exe.lto.bc fail.log lib20.a main21.preload lib21.a
+ rm -rf *.o main*.exe big.* *.dylib main16.*.bc fail.log lib20.a main21.preload lib21.a
# symbol from a dylib.
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
-
run: all
all:
${CC} ${CCFLAGS} foo.c -c -o foo.o
libtool -static foo.o -o libfoo.a
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
- ${LLVMGCC} ${CCFLAGS} main.o -o main libfoo.a
+ ${CC} ${CCFLAGS} -flto main.c -c -o main.o
+ ${CC} ${CCFLAGS} main.o -o main libfoo.a
${PASS_IFF_GOOD_MACHO} main
clean:
# <rdar://problem/6806033> Link Time Optimization error with 'dead code strip' + hidden symbol
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o -fvisibility=hidden
- ${LLVMGCC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip
+ ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o -fvisibility=hidden
+ ${CC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip
${PASS_IFF_GOOD_MACHO} libbar.dylib
clean:
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+# Test case for <rdar://problem/9777977>
+# mach-o has coalescable strings which are dead stripped away
+# but come back from bitcode file.
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} foo.c -c -flto -o foo.o
+ ${CC} ${CCFLAGS} main.c -c -o main.o
+ ${CC} ${CCFLAGS} main.o foo.o -o main -dead_strip -framework Foundation
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm -rf main main.o foo.o
--- /dev/null
+
+#include <string.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+void foo1()
+{
+ CFStringGetLength(CFSTR("test1"));
+ strlen("str1");
+}
+
+void foo2()
+{
+ CFStringGetLength(CFSTR("test2"));
+ strlen("str2");
+}
+
+void foo3()
+{
+ CFStringGetLength(CFSTR("test3"));
+ strlen("str3");
+}
--- /dev/null
+
+#include <string.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+
+extern void foo1();
+
+
+void t1()
+{
+ CFStringGetLength(CFSTR("test1"));
+ strlen("str1");
+}
+
+void t2()
+{
+ CFStringGetLength(CFSTR("test2"));
+ strlen("str2");
+}
+
+void t3()
+{
+ CFStringGetLength(CFSTR("test3"));
+ strlen("str3");
+}
+
+
+int main()
+{
+ t2();
+ foo1();
+ return 0;
+}
+
# <rdar://problem/8481987> Link Time Optimization crashes linker with 'dead code strip' + hidden symbol
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm foo.m -c -o foo.o -fvisibility=hidden
- ${LLVMGCC} ${CCFLAGS} -dynamiclib foo.o -o libfoo.dylib -dead_strip -framework Foundation
+ ${CC} ${CCFLAGS} -flto foo.m -c -o foo.o -fvisibility=hidden
+ ${CC} ${CCFLAGS} -dynamiclib foo.o -o libfoo.dylib -dead_strip -framework Foundation
${PASS_IFF_GOOD_MACHO} libfoo.dylib
clean:
# <rdar://problem/8481987> Link Time Optimization crashes linker with 'dead code strip' + hidden symbol
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o -fvisibility=hidden
- ${LLVMGCC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip
+ ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o -fvisibility=hidden
+ ${CC} ${CCFLAGS} -dynamiclib bar.o -o libbar.dylib -dead_strip
${PASS_IFF_GOOD_MACHO} libbar.dylib
clean:
# <rdar://problem/8708091> Link Time Optimization error with tentative defs and -dead_strip
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm foo.c -c -o foo.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm baz.c -c -o baz.o
- ${LLVMGCC} ${CCFLAGS} main.c -c -o main.o
- ${LLVMGCC} ${CCFLAGS} main.o foo.o bar.o baz.o -o main -dead_strip
+ ${CC} ${CCFLAGS} -flto foo.c -c -o foo.o
+ ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o
+ ${CC} ${CCFLAGS} -flto baz.c -c -o baz.o
+ ${CC} ${CCFLAGS} main.c -c -o main.o
+ ${CC} ${CCFLAGS} main.o foo.o bar.o baz.o -o main -dead_strip
${PASS_IFF_GOOD_MACHO} main
clean:
# <rdar://problem/7438246> LTO with 'dead code strip' can't ignore unused functions with undefined references
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
-
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm bar.c -c -o bar.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
- ${LLVMGCC} ${CCFLAGS} -dead_strip main.o bar.o -o main
+ ${CC} ${CCFLAGS} -flto bar.c -c -o bar.o
+ ${CC} ${CCFLAGS} -flto main.c -c -o main.o
+ ${CC} ${CCFLAGS} -dead_strip main.o bar.o -o main
${PASS_IFF_GOOD_MACHO} main
clean:
# results in foo() not being inlined
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
-
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o -fvisibility=hidden
- ${LLVMGCC} ${CCFLAGS} main.o -o main
+ ${CC} ${CCFLAGS} -flto main.c -c -o main.o -fvisibility=hidden
+ ${CC} ${CCFLAGS} main.o -o main
nm main | grep _foo | ${FAIL_IF_STDIN}
- ${LLVMGCC} ${CCFLAGS} main.o -o main2 -fvisibility=hidden -Wl,-mllvm -Wl,--disable-inlining
+ ${CC} ${CCFLAGS} main.o -o main2 -fvisibility=hidden -Wl,-mllvm -Wl,--disable-inlining
nm main2 | grep _foo | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
# Check that LTO can bring in an ObjC class from an archive member
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
run: all
${CC} ${CCFLAGS} foo2.c -c -o foo2.o
${CC} ${CCFLAGS} bar2.c -c -o bar2.o
libtool -static foo.o bar.o foo2.o bar2.o -o libfoobar.a
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.m -c -o main.o
- ${LLVMGCC} ${CCFLAGS} main.o -o main libfoobar.a -framework Foundation
+ ${CC} ${CCFLAGS} -flto main.m -c -o main.o
+ ${CC} ${CCFLAGS} main.o -o main libfoobar.a -framework Foundation
${PASS_IFF_GOOD_MACHO} main
clean:
# <rdar://problem/8198537> trivial Objective-C app fails when using libLTO
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
-
IMAGE_INFO = "__image_info"
ifeq ($(ARCH),x86_64)
IMAGE_INFO = "__objc_imageinfo"
endif
-ifeq ($(ARCH),armv6)
+ifeq ($(FILEARCH),arm)
IMAGE_INFO = "__objc_imageinfo"
endif
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.m -c -o main.o
- ${LLVMGCC} ${CCFLAGS} main.o -o main -framework Foundation
+ ${CC} ${CCFLAGS} -flto main.m -c -o main.o
+ ${CC} ${CCFLAGS} main.o -o main -framework Foundation
size -l main | grep ${IMAGE_INFO} | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
# being produced.
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
run: all
all:
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o -fvisibility=hidden
- ${LLVMGCC} ${CCFLAGS} main.o -o main -Wl,-object_path_lto,`pwd`/main.tmp.o
+ ${CC} ${CCFLAGS} -flto main.c -c -o main.o -fvisibility=hidden
+ ${CC} ${CCFLAGS} main.o -o main -Wl,-object_path_lto,`pwd`/main.tmp.o
nm main.tmp.o | grep _main | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
# and mach-o has a strong _foo.
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
-
run: all
all:
${CC} ${CCFLAGS} foo.c -c -o foo.o
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
- ${LLVMGCC} ${CCFLAGS} main.o foo.o -o main
+ ${CC} ${CCFLAGS} -flto main.c -c -o main.o
+ ${CC} ${CCFLAGS} main.o foo.o -o main
otool -Iv main | grep _abort | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} main
int main()
{
foo();
-
+ return 0;
}
# Check that LTO can bring in an ObjC class from an archive member
#
-LLVMGCC = /Developer/usr/bin/llvm-gcc-4.2 -arch ${ARCH}
-LLVMGXX = /Developer/usr/bin/llvm-g++-4.2 -arch ${ARCH}
-
run: all
all:
${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
- ${LLVMGCC} ${CCFLAGS} --emit-llvm main.c -c -o main.o
- ${LLVMGCC} ${CCFLAGS} main.o -o main libfoo.dylib
+ ${CC} ${CCFLAGS} -flto main.c -c -o main.o
+ ${CC} ${CCFLAGS} main.o -o main libfoo.dylib
${PASS_IFF_GOOD_MACHO} main
clean:
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Check that the -merge_zero_fill_sections works
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} main.c -o main -Wl,-merge_zero_fill_sections
+ size -l main | grep __bss | ${FAIL_IF_STDIN}
+ size -l main | grep __common | ${FAIL_IF_STDIN}
+ size -l main | grep __zerofill | grep "offset 0" | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm main
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 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>
+
+static int a[2000];
+int b[2000];
+
+extern void* zerofill_start __asm("section$start$__DATA$__zerofill");
+extern void* zerofill__end __asm("section$end$__DATA$__zerofill");
+
+void* start = &zerofill_start;
+void* end = &zerofill__end;
+
+int main()
+{
+ a[0] = 0;
+ b[0] = 0;
+ return 0;
+}
+
.text
+ .align 4
.globl _test
_test:
#if __i386__
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+SHELL = bash # use bash shell so we can redirect just stderr
+
+#
+# Verify optimization in which categories are merged into classes
+#
+OPTIONS =
+
+ifeq ($(ARCH),i386)
+ OPTIONS = -fobjc-abi-version=2 -Wl,-objc_abi_version,2
+endif
+
+all: all-${FILEARCH}
+
+all-ppc:
+ ${PASS_IFF} true
+
+all-i386: all-rest
+all-x86_64: all-rest
+all-arm: all-rest
+
+all-rest:
+ ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat.m -DOVERRIDE_CLASS=1 -framework Foundation -o libfoo.dylib 2> warnings1.txt
+ grep warning warnings1.txt | grep instance_method | ${FAIL_IF_EMPTY}
+ grep warning warnings1.txt | grep class_method | ${FAIL_IF_EMPTY}
+ ${CC} ${CCFLAGS} ${OPTIONS} -dynamiclib foo.m cat.m copycat.m -framework Foundation -o libfoo2.dylib 2> warnings2.txt
+ grep warning warnings2.txt | grep instance_method_fromcat | ${FAIL_IF_EMPTY}
+ grep warning warnings2.txt | grep class_method_fromcat | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} libfoo2.dylib
+
+clean:
+ rm -rf lib*.dylib warnings*.txt
\ No newline at end of file
--- /dev/null
+#include <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+-(void) method1;
+@end
+
+@interface Foo(fromcat)
+- (void) instance_method_fromcat;
++ (void) class_method_fromcat;
+#if OVERRIDE_CLASS
+- (void) instance_method;
++ (void) class_method;
+#endif
+@end
+
+@implementation Foo(fromcat)
+- (void) instance_method_fromcat {}
++ (void) class_method_fromcat {}
+#if OVERRIDE_CLASS
+- (void) instance_method {}
++ (void) class_method {}
+#endif
+@end
+
--- /dev/null
+#include <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+-(void) method1;
+@end
+
+@interface Foo(copycat)
+- (void) instance_method_fromcat;
++ (void) class_method_fromcat;
+@end
+
+@implementation Foo(copycat)
+- (void) instance_method_fromcat {}
++ (void) class_method_fromcat {}
+@end
+
--- /dev/null
+#include <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+-(void) instance_method;
++(void) class_method;
+@end
+
+
+@implementation Foo
+-(void) instance_method {}
++(void) class_method {}
+@end
+
ifeq (${ARCH},x86_64)
CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo'
endif
+ifeq (${FILEARCH},arm)
+ CLASS_NAME_FOO = '_OBJC_CLASS_$$_Foo'
+endif
run: all
ifeq ($(ARCH),x86_64)
IMAGE_INFO = "__objc_imageinfo"
endif
-ifeq ($(ARCH),armv6)
- IMAGE_INFO = "__objc_imageinfo"
-endif
+test: test-${FILEARCH}
+test-i386: test-macosx
+test-x86_64: test-macosx
+test-arm: test-good
#
# Validate that the linker catches illegal combinations of .o files
# compiled with different GC settings.
#
-test:
+test-macosx:
${CC} ${CCFLAGS} foo.m -c -o foo.o
${FAIL_IF_BAD_OBJ} foo.o
${CC} ${CCFLAGS} foo.o runtime.c -dynamiclib -o libfoo.dylib
${FAIL_IF_SUCCESS} ${CC} ${CCFLAGS} bar-gc-only.o runtime.c -dynamiclib libfoo.dylib -o libbar.dylib 2> fail.log
+ ${PASS_IFF} true
+test-good:
${PASS_IFF} true
clean:
all:
${CC} ${CCFLAGS} test.m -dynamiclib -framework Foundation -o libtest.dylib
- ${CC} ${CCFLAGS} test.m -dynamiclib -framework Foundation -o libtest.dylib -Wl,-no_compact_linkedit -ldylib1.o
${PASS_IFF_GOOD_MACHO} libtest.dylib
clean:
.text
+ .align 4
.globl _foo1
_foo1: nop
+++ /dev/null
-
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-SHELL = bash # use bash shell so we can redirect just stderr
-
-#
-# Verify -prebind for 10.3 make ppc prebound and all others not prebound
-#
-
-ifeq (,${findstring 64,$(ARCH)})
- ifeq (${ARCH},i386)
- KEYWORD = NOUNDEFS
- else
- KEYWORD = PREBOUND
- endif
-else
- KEYWORD = NOUNDEFS
-endif
-
-
-run: all
-
-all:
- # SnowLeopard is missing libmx.dylib which gcc thinks it needs
- ${CC} ${CCFLAGS} main.c -o main -prebind -mmacosx-version-min=10.3 -nostdlib -lcrt1.o -lSystem
- otool -hv main | grep ${KEYWORD} | ${FAIL_IF_EMPTY}
- ${PASS_IFF_GOOD_MACHO} main
-
-clean:
- rm main
+++ /dev/null
-
-int main() { return 0; }
-
# The point of this test is to build a prebound split-seg library
#
-run: all
+all: all-${ARCH}
-all:
+all-i386: all-32
+all-ppc: all-32
+all-x86_64: all-good
+all-armv6: all-good
+all-armv7: all-good
+
+
+all-good:
+ ${PASS_IFF} true
+
+
+all-32:
${CC} ${CCFLAGS} -seg_addr_table address_table -prebind bar.c -dynamiclib -o libbar.dylib -install_name /foo/bar/libbar.dylib
${PASS_IFF_GOOD_MACHO} libbar.dylib
clean:
- rm *.dylib
+ rm -rf *.dylib
${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib
# verify uses of symbols from re-exported dylibs have ordinal that allows dyld to search amoung re-exported dylibs
${CC} ${CCFLAGS} -dynamiclib foo.c -DUSE_BAZ -o libfoo2.dylib -Wl,-reexport_library libpub.dylib -Wl,-reexport_library libbar.dylib -Wl,-reexport_library libbaz.dylib
- dyldinfo -bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY}
- dyldinfo -lazy_bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY}
- dyldinfo -bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY}
- dyldinfo -lazy_bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -lazy_bind libfoo2.dylib | grep _bar | grep this-image | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -lazy_bind libfoo2.dylib | grep _baz | grep this-image | ${FAIL_IF_EMPTY}
# verify that if only one non-publc re-exported dylib, don't use magic ordinal
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo1.dylib -Wl,-reexport_library libpub.dylib -Wl,-reexport_library libbar.dylib
- dyldinfo -bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN}
- dyldinfo -lazy_bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN}
+ ${DYLDINFO} -bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN}
+ ${DYLDINFO} -lazy_bind libfoo1.dylib | grep _bar | grep this-image | ${FAIL_IF_STDIN}
${PASS_IFF_GOOD_MACHO} libfoo2.dylib
#
-run: all
+all: all-${ARCH}
-all:
+all-i386: all-new all-old
+all-x86_64: all-new all-old
+all-armv6: all-new
+all-armv7: all-new
-# -sub_library for 10.4
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} libfoo.dylib
- otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY}
- otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
-
-# -sub_library for 10.5
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.5
+
+all-new:
+ # -sub_library for 10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} libfoo.dylib
otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN}
-
-
-# -sub_umbrella for 10.4
- mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} Foo.framework/Foo
- otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_EMPTY}
- otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
-# -sub_umbrella for 10.5
+ # -sub_umbrella for 10.5
mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} Foo.framework/Foo
otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_STDIN}
-
-# -umbrella for 10.4
+ # -umbrella for 10.5
mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} Foo.framework/Foo
- otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY}
- otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
-
-# -umbrella for 10.5
- mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.5
- ${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} Foo.framework/Foo
otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY}
otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
-
-# -reexport_library for 10.4
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.4
+ # -reexport_library for 10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} libfoo.dylib
- otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY}
- otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
+ otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
+ otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN}
-# -reexport_library for 10.5
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.5
+ # -reexport-l for 10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. ${VERSION_NEW_LINKEDIT}
${FAIL_IF_BAD_MACHO} libfoo.dylib
otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN}
+
+ # -reexport_framework for 10.5
+ mkdir -p Bar.framework Foo.framework
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_NEW_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} Bar.framework/Bar
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_NEW_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} Foo.framework/Foo
+ otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
+ otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_STDIN}
+ # -reexport_framework and -umbrella for 10.4
+ mkdir -p Bar.framework Foo.framework
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_NEW_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} Bar.framework/Bar
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_NEW_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} Foo.framework/Foo
+ otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY}
+ otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
+
+ ${PASS_IFF} /usr/bin/true
-# -reexport-l for 10.4
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.4
+
+
+all-old:
+ # -sub_library for 10.4
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} libfoo.dylib
otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY}
otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
-# -reexport-l for 10.5
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -mmacosx-version-min=10.5
- ${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. -mmacosx-version-min=10.5
- ${FAIL_IF_BAD_MACHO} libfoo.dylib
- otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
- otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_STDIN}
-
-
-# -reexport_framework for 10.4
+ # -sub_umbrella for 10.4
mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar -sub_umbrella Bar ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Foo.framework/Foo
otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_EMPTY}
- otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
+ otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
-# -reexport_framework for 10.5
+ # -umbrella for 10.4
mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -framework Bar ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Foo.framework/Foo
- otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
- otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_STDIN}
-
+ otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY}
+ otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
+
+ # -reexport_library for 10.4
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_OLD_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} libbar.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib ${VERSION_OLD_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} libfoo.dylib
+ otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY}
+ otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
-# -reexport_framework and -umbrella for 10.4
+ # -reexport-l for 10.4
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib ${VERSION_OLD_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} libbar.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport-lbar -L. ${VERSION_OLD_LINKEDIT}
+ ${FAIL_IF_BAD_MACHO} libfoo.dylib
+ otool -lv libfoo.dylib | grep LC_SUB_LIBRARY | ${FAIL_IF_EMPTY}
+ otool -lv libfoo.dylib | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
+
+ # -reexport_framework for 10.4
mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Foo.framework/Foo
- otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY}
+ otool -lv Foo.framework/Foo | grep LC_SUB_UMBRELLA | ${FAIL_IF_EMPTY}
otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
-
-# -reexport_framework and -umbrella for 10.4
+ # -reexport_framework and -umbrella for 10.4
mkdir -p Bar.framework Foo.framework
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o Bar.framework/Bar -install_name "`pwd`/Bar.framework/Bar" -umbrella Foo ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Bar.framework/Bar
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o Foo.framework/Foo -F. -Wl,-reexport_framework,Bar ${VERSION_OLD_LINKEDIT}
${FAIL_IF_BAD_MACHO} Foo.framework/Foo
otool -lv Bar.framework/Bar | grep LC_SUB_FRAMEWORK | ${FAIL_IF_EMPTY}
- otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_EMPTY}
+ otool -lv Foo.framework/Foo | grep LC_REEXPORT_DYLIB | ${FAIL_IF_STDIN}
${PASS_IFF} /usr/bin/true
#
-run: all
-all:
+all: all-${ARCH}
+all-i386: all-new all-old
+all-x86_64: all-new all-old
+all-armv6: all-new
+all-armv7: all-new
-# -reexport_library for 10.4
- ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} libbaz.dylib
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.4
- ${FAIL_IF_BAD_MACHO} libfoo.dylib
- ${CC} ${CCFLAGS} main.c libfoo.dylib -o main
- ${FAIL_IF_BAD_MACHO} main
-
-# -reexport_library for 10.5 and later
+all-new:
+ # -reexport_library for 10.5 and later
${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib
${FAIL_IF_BAD_MACHO} libbaz.dylib
${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib
${CC} ${CCFLAGS} main.c libfoo.dylib -o main
${PASS_IFF_GOOD_MACHO} main
+all-old:
+ # -reexport_library for 10.4
+ ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -mmacosx-version-min=10.4
+ ${FAIL_IF_BAD_MACHO} libbaz.dylib
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib -mmacosx-version-min=10.4
+ ${FAIL_IF_BAD_MACHO} libbar.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-reexport_library,libbar.dylib -mmacosx-version-min=10.4
+ ${FAIL_IF_BAD_MACHO} libfoo.dylib
+ ${CC} ${CCFLAGS} main.c libfoo.dylib -o main
+ ${FAIL_IF_BAD_MACHO} main
+
+
clean:
#
-run: all
+all: all-${ARCH}
-all:
+all-i386: all-new all-old
+all-x86_64: all-new all-old
+all-armv6: all-new
+all-armv7: all-new
-# -sub_library for 10.4
+all-old:
+ # -sub_library for 10.4
${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4
${FAIL_IF_BAD_MACHO} libbar.dylib
${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.4
${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.4
nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
-
-# -sub_library for 10.5
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5
+
+all-new:
+ # -sub_library for 10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib middle.c -o libmiddle.dylib -lbar -L. -sub_library libbar -install_name /mid/libmiddle.dylib
${FAIL_IF_BAD_MACHO} libmiddle.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lmiddle -L. -sub_library libmiddle -install_name /usr/lib/libfoo.dylib
${FAIL_IF_BAD_MACHO} libfoo.dylib
- ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib other.c -o libother.dylib -lbar -L. -sub_library libbar -install_name /usr/lib/libother.dylib
${FAIL_IF_BAD_MACHO} libother.dylib
- ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L. -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib libother.dylib -o main -L.
nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
#
-run: all
-all:
+all: all-${ARCH}
-# -sub_library for 10.4
+all-i386: all-new all-old
+all-x86_64: all-new all-old
+all-armv6: all-new
+all-armv7: all-new
+
+
+all-old:
+ # -sub_library for 10.4
${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.4
${FAIL_IF_BAD_MACHO} libbar.dylib
${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.4
${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. -mmacosx-version-min=10.4
otool -L main | grep libbar | ${FAIL_IF_STDIN}
-
-# -sub_library for 10.5
- ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib -mmacosx-version-min=10.5
+
+all-new:
+ # -sub_library for 10.5
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name /usr/lib/libbar.dylib
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -lbar -L. -sub_library libbar
${FAIL_IF_BAD_MACHO} libfoo.dylib
- ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L. -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} main.c -DCALL_BAR libfoo.dylib -o main -L.
otool -L main | grep libbar | ${FAIL_IF_EMPTY}
nm -m main | grep _bar | grep libbar | ${FAIL_IF_EMPTY}
- ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L. -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} main.c libfoo.dylib -o main -L.
otool -L main | grep libbar | ${FAIL_IF_STDIN}
-##
+ ##
# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
XRELOCS_NEEDED = ${FAIL_IF_STDIN}
LRELOCS_NEEDED = ${FAIL_IF_STDIN}
endif
+ifeq (${FILEARCH},arm)
+ NO_PIC = -mdynamic-no-pic
+ STATIC = -static
+endif
.text
_pointer_diffs:
- .long _foo-1b
+1: .long _foo-1b
.long _foo+10-1b
.long _test_branches-1b
.long _test_branches+3-1b
movt r0, :upper16:_datahilo16alt+1792
movw r0, :lower16:_datahilo16alt+165
movt r0, :upper16:_datahilo16alt+165
+ movw r0, :lower16:_thumbTarget
+ movt r0, :upper16:_thumbTarget
+ movw r0, :lower16:_externalTarget
+ movt r0, :upper16:_externalTarget
+ movw r0, :lower16:_externalTarget+61447
+ movt r0, :upper16:_externalTarget+61447
Lpicbase:
movw r0, :lower16:_datahilo16 - Lpicbase
movt r0, :upper16:_datahilo16 - Lpicbase
movt r0, :upper16:_datahilo16alt+1792 - Lpicbase
movw r0, :lower16:_datahilo16alt+165 - Lpicbase
movt r0, :upper16:_datahilo16alt+165 - Lpicbase
+ movw r0, :lower16:_thumbTarget - Lpicbase
+ movt r0, :upper16:_thumbTarget - Lpicbase
bx lr
.code 16
movt r0, :upper16:_datahilo16alt+1792
movw r0, :lower16:_datahilo16alt+165
movt r0, :upper16:_datahilo16alt+165
+ movw r0, :lower16:_thumbTarget
+ movt r0, :upper16:_thumbTarget
+ movw r0, :lower16:_externalTarget
+ movt r0, :upper16:_externalTarget
+ movw r0, :lower16:_externalTarget+61447
+ movt r0, :upper16:_externalTarget+61447
Lpicbase2:
movw r0, :lower16:_datahilo16 - Lpicbase2
movt r0, :upper16:_datahilo16 - Lpicbase2
movt r0, :upper16:_datahilo16alt+1792 - Lpicbase2
movw r0, :lower16:_datahilo16alt+165 - Lpicbase2
movt r0, :upper16:_datahilo16alt+165 - Lpicbase2
+ movw r0, :lower16:_thumbTarget - Lpicbase2
+ movt r0, :upper16:_thumbTarget - Lpicbase2
bx lr
-
+
+ .code 16
+ .thumb_func _thumbTarget
+_thumbTarget:
+ nop
+ bx lr
+
.data
_datahilo16: .long 0
_datahilo16alt: .long 0
+
+
#endif
#endif
.long _external+16
.long _test_weak
.long _test_weak+16
+ .long Lother - . + 0x4000000
+Lother:
+ .long 0
#elif __ppc64__ || __x86_64__
.quad _test_calls
.quad _test_calls+16
.quad _external+16
.quad _test_weak
.quad _test_weak+16
+ .quad Lother - . + 0x4000000
+Lother:
+ .quad 0
#endif
# test that reloc sizes are the same
-void foo() {}
+
+int x;
+
+int foo() { return x; }
run: all
all:
- $(CXX) -gstabs+ main.c -o outfile
+ $(CC) -gstabs+ main.c -o outfile
${FAIL_IF_BAD_MACHO} outfile
- nm -ap outfile | ${PASS_IFF} grep '.*\<SO\>.*test-cases.*/$$'
+ nm -ap outfile | grep '.*\<SO\>.*test-cases.*/$$' | ${PASS_IFF_STDIN}
clean:
rm outfile*
-main()
+int main()
{
+ return 0;
}
# Test the ld option -stack_addr and -stack_size used together
-ifeq ($(ARCH),armv6)
+ifeq ($(FILEARCH),arm)
STACK_ADDR = 0x2C000000
STACK_SIZE = 0x05000000
STACK_TOP = 0x27000000
otool -l main | grep -A6 __UNIXSTACK > main.otool
grep " vmsize[ ]*${STACK_SIZE}" main.otool | ${FAIL_IF_EMPTY}
grep " vmaddr[ ]*${STACK_TOP}" main.otool | ${FAIL_IF_EMPTY}
- ${PASS_IFF_GOOD_MACHO} main
+ ${FAIL_IF_BAD_MACHO} main
+ ${CC} ${CCFLAGS} -static main.c -o main2 -e _main -nostdlib -Wl,-new_linker -Wl,-stack_size,${STACK_SIZE} -Wl,-stack_addr,${STACK_ADDR}
+ otool -l main2 | grep -A6 __UNIXSTACK > main2.otool
+ grep " vmsize[ ]*${STACK_SIZE}" main2.otool | ${FAIL_IF_EMPTY}
+ grep " vmaddr[ ]*${STACK_TOP}" main2.otool | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main2
clean:
- rm -rf main main.otool
+ rm -rf main main.otool main2.otool main2
# Test the ld option -stack_size adds a custom stack segment
-ifeq ($(ARCH),armv6)
+ifeq (${FILEARCH},arm)
STACK_ADDR = 0x30000000
STACK_SIZE = 0x05000000
- STACK_TOP = 0x2b000000
+ STACK_TOP = 0x2a000000
else
ifeq (,${findstring 64,$(ARCH)})
STACK_ADDR = 0xC0000000
int main()
{
func(data);
+ return 0;
}
# <rdar://problem/5847206> SPEC2000/eon built with -mdynamic-no-pic won't run
#
-run: test-run-${ARCH}
+all: test-${FILEARCH}
-test-run-ppc:
- ${PASS_IFF} true
-
-test-run-x86_64:
- ${PASS_IFF} true
-
-test-run-armv6: test-run-i386
+test-ppc: test-good
+test-x86_64: test-good
+test-i386: test
+test-arm: test
-test-run-i386:
+test:
# check jump table in a weak function
- ${CC} ${CCFLAGS} main.c switch.s -o main
+ ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-no_pie
otool -rv main | grep _foo | ${FAIL_IF_STDIN}
otool -rv main | grep _bar | ${FAIL_IF_STDIN}
# check jump table in a regular function with -flat_namespace
- ${CC} ${CCFLAGS} main.c switch.s -o main -flat_namespace
+ ${CC} ${CCFLAGS} main.c switch.s -o main -flat_namespace -Wl,-no_pie
otool -rv main | grep _foo | ${FAIL_IF_STDIN}
otool -rv main | grep _bar | ${FAIL_IF_STDIN}
# check jump table in a regular function that is interposable
- ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-interposable_list,interpose.exp
+ ${CC} ${CCFLAGS} main.c switch.s -o main -Wl,-interposable_list,interpose.exp -Wl,-no_pie
otool -rv main | grep _foo | ${FAIL_IF_STDIN}
otool -rv main | grep _bar | ${FAIL_IF_STDIN}
# check jump table with -pie, should have no external and some local relocations
# otool -rv main | grep "Local relocation" | ${FAIL_IF_EMPTY}
${PASS_IFF_GOOD_MACHO} main
+test-good:
+ ${PASS_IFF} true
clean:
rm -f main
*/
.globl _foo
.weak_definition _foo
+ .align 4
_foo:
nop
nop
to a jump table
*/
.text
+ .align 4
.globl _bar
_bar: nop
nop
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+SHELL = bash # use bash shell so we can redirect just stderr
+
+
+#
+# Test how tentative definitions interact with archives
+# main.c has a tenative definition for _foo which
+# should cause libfoo_data.a to be loaded, but not libfoo_tent.a
+# nor libfoo_code.a.
+#
+# <rdar://problem/7890410> Snow Leopard linker bug with common symbols
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} junk.c -c -o junk.o
+ # verify data symbol does get pulled in
+ ${CC} ${CCFLAGS} foo_data.c -c -o foo_data.o
+ libtool -static junk.o foo_data.o -o libfoo_data.a
+ ${CC} ${CCFLAGS} main.c libfoo_data.a -o main_data
+ nm -m main_data | grep _foo | grep __TEXT | ${FAIL_IF_STDIN}
+ # verify tentative does not
+ ${CC} ${CCFLAGS} foo_tent.c -c -o foo_tent.o
+ libtool -static junk.o foo_tent.o -o libfoo_tent.a
+ ${CC} ${CCFLAGS} main.c libfoo_tent.a -o main_tent
+ nm -m main_tent | grep _foo | grep __TEXT | ${FAIL_IF_STDIN}
+ # verify code does not
+ ${CC} ${CCFLAGS} foo_code.c -c -o foo_code.o
+ libtool -static junk.o foo_code.o -o libfoo_code.a
+ ${CC} ${CCFLAGS} main.c libfoo_code.a -o main_code
+ nm -m main_code | grep _foo | grep __TEXT | ${FAIL_IF_STDIN}
+ # verify warning when code force to override tent
+ ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoo_code.a -o main_code_force 2>main_code_force_warnings.txt
+ grep warning main_code_force_warnings.txt | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main_code
+
+clean:
+ rm -rf junk.o foo_data.o libfoo_data.a main_data foo_tent.o libfoo_tent.a main_tent foo_code.o libfoo_code.a main_code main_code_force main_code_force_warnings.txt
+
--- /dev/null
+
+int foo() { return 10; }
+
--- /dev/null
+
+int foo = 5;
--- /dev/null
+
+int foo;
--- /dev/null
+void junk() {}
--- /dev/null
+
+int foo;
+
+int main()
+{
+ foo = 3;
+ return 0;
+}
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Check that main executable can use TLVs with -dead_strip
+#
+
+run: all
+
+all:
+ clang ${CCFLAGS} main.c -o main -dead_strip -mmacosx-version-min=10.7
+ otool -lv main | grep S_THREAD_LOCAL_VARIABLES | ${FAIL_IF_EMPTY}
+ otool -lv main | grep S_THREAD_LOCAL_REGULAR | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main
+
+clean:
+ rm main
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 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@
+ */
+
+__thread int gTLSThreadID = 0;
+
+int foobar() {
+ return gTLSThreadID;
+}
+
+int main(void) {
+ return foobar();
+}
${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib
# re-link against correct dylibs now that everything is built
${CC} ${CCFLAGS} -dynamiclib a.c -o liba.dylib libc.dylib -install_name /usr/local/lib/big/liba.dylib -umbrella Big
- dyldinfo -lazy_bind liba.dylib | grep c2 | grep libc | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -lazy_bind liba.dylib | grep c2 | grep libc | ${FAIL_IF_EMPTY}
${CC} ${CCFLAGS} -dynamiclib b.c -o libb.dylib libc.dylib -install_name /usr/local/lib/big/libb.dylib -umbrella Big
${CC} ${CCFLAGS} -dynamiclib c.c -o libc.dylib liba.dylib -install_name /usr/local/lib/big/libc.dylib -umbrella Big
- dyldinfo -lazy_bind libc.dylib | grep a2 | grep liba | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -lazy_bind libc.dylib | grep a2 | grep liba | ${FAIL_IF_EMPTY}
${CC} ${CCFLAGS} -dynamiclib -Wl,-reexport_library,liba.dylib -Wl,-reexport_library,libb.dylib -Wl,-reexport_library,libc.dylib -o libBig.dylib -install_name /usr/lib/libBig.dylib
${CC} ${CCFLAGS} main.c -o main libBig.dylib -L.
${PASS_IFF_GOOD_MACHO} main
.text
-
+ .align 4
+
.globl _my_weak
.weak_def_can_be_hidden _my_weak
_my_weak: nop
--- /dev/null
+##
+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+SHELL = bash # use bash shell so we can redirect just stderr
+
+
+#
+# <rdar://problem/9551362> clang ld: bad codegen, pointer diff
+# Test that codegen needing direct access to a weak symbol just issues
+# a warning. Check that if export list makes weak symbol hidden, there is
+# no warning.
+#
+
+run: all
+
+all:
+ ${CC} ${CCFLAGS} myglobal.c myhidden.s -dynamiclib -o libmy.dylib 2>warnings.txt
+ grep "global weak" warnings.txt | ${FAIL_IF_EMPTY}
+ ${FAIL_IF_BAD_MACHO} libmy.dylib
+ ${CC} ${CCFLAGS} myglobal.c myhidden.s -dynamiclib -Wl,-exported_symbol,_test -o libmy2.dylib
+ ${PASS_IFF_GOOD_MACHO} libmy2.dylib
+
+clean:
+ rm -f libmy.dylib libmy2.dylib warnings.txt
--- /dev/null
+
+__attribute__((weak))
+int myweak = 10;
+
--- /dev/null
+ .data
+ .globl _myweak
+ .private_extern _myweak
+ .weak_definition _myweak
+_myweak:
+ .long 0
+
+
+ .text
+ .align 2
+#if __ARM_ARCH_7A__
+ .code 16
+ .thumb_func _test
+#endif
+
+ .globl _test
+_test:
+#if __x86_64__
+ nop
+ movl _myweak(%rip), %eax
+ ret
+#elif __i386__
+ call L1
+L1: popl %eax
+ movl _myweak-L1(%eax), %eax
+ ret
+#elif __arm__
+
+#if __ARM_ARCH_7A__
+ movw r0, :lower16:(_myweak-(L4+4))
+ movt r0, :upper16:(_myweak-(L4+4))
+L4: add r0, pc
+ ldr r0, [r0]
+ bx lr
+#else
+ ldr r0, L2
+L3: ldr r0, [pc, r0]
+ bx lr
+ .align 2
+L2: .long _myweak-(L3+8)
+#endif
+
+
+#endif
+
${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib
${FAIL_IF_BAD_MACHO} libbar.dylib
- ${CC} ${CCFLAGS} -mmacosx-version-min=10.4 main.c -o main libfoo.dylib libbar.dylib
+ ${CC} ${CCFLAGS} ${VERSION_OLD_LINKEDIT} main.c -o main libfoo.dylib libbar.dylib
# libfoo.dylib should be weakly loaded because all symbols are weakly imported
otool -lv main | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY}
- # libbar.dylib should not be weakly loaded because _bar4 is not weakly imported
+ # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported
otool -lv main | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY}
- ${PASS_IFF_GOOD_MACHO} main
+ ${FAIL_IF_BAD_MACHO} main
+
+ ${CC} ${CCFLAGS} ${VERSION_NEW_LINKEDIT} main.c -o main2 libfoo.dylib libbar.dylib
+ # libfoo.dylib should be weakly loaded because all symbols are weakly imported
+ otool -lv main2 | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY}
+ # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported
+ otool -lv main2 | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} main2
+
+ ${CC} ${CCFLAGS} ${VERSION_NEW_LINKEDIT} data.c -o data libfoo.dylib libbar.dylib
+ # libfoo.dylib should be weakly loaded because all symbols are weakly imported
+ otool -lv data | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY}
+ # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported
+ otool -lv data | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY}
+ ${FAIL_IF_BAD_MACHO} data
+ ${CC} ${CCFLAGS} ${VERSION_NEW_LINKEDIT} data.c -o data2 libfoo.dylib libbar.dylib
+ # libfoo.dylib should be weakly loaded because all symbols are weakly imported
+ otool -lv data2 | grep -B2 libfoo.dylib | grep LC_LOAD_WEAK_DYLIB | ${FAIL_IF_EMPTY}
+ # libbar.dylib should not be weakly loaded because _bar1 is not weakly imported
+ otool -lv data2 | grep -B2 libbar.dylib | grep LC_LOAD_DYLIB | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} data2
clean:
- rm -rf libfoo.dylib libbar.dylib main
+ rm -rf libfoo.dylib libbar.dylib main main2 data data2
--- /dev/null
+
+#include "foo.h"
+#include "bar.h"
+
+void* pfoo4 = &foo4;
+void* pfoo2 = &foo2;
+
+void* pbar2 = &bar2;
+void* pbar1 = &bar1; // not weak
+
+int main (void)
+{
+ return 0;
+}
+
--- /dev/null
+##
+# Copyright (c) 2011 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+#
+# Test the weak_import attribute works
+#
+
+all: all-${ARCH}
+
+all-ppc: all-true
+
+all-ppc64: all-true
+
+all-i386: all-true
+
+all-armv6: all-true
+
+all-armv7: all-true
+
+all-true:
+ ${PASS_IFF} true
+
+all-x86_64:
+ ${CC} ${CCFLAGS} -dynamiclib test.s -o libtest.dylib -mmacosx-version-min=10.6
+ ${DYLDINFO} -bind -lazy_bind libtest.dylib | grep _malloc | grep weak | ${FAIL_IF_EMPTY}
+ ${DYLDINFO} -bind -lazy_bind libtest.dylib | grep _free | grep weak | ${FAIL_IF_EMPTY}
+ ${PASS_IFF_GOOD_MACHO} libtest.dylib
+
+clean:
+ rm -rf libtest.dylib
--- /dev/null
+
+
+ .text
+_foo:
+#if __x86_64__
+ .weak_reference _malloc
+ .weak_reference _free
+ cmpq $0, _malloc@GOTPCREL(%rip)
+ cmpq $0xFFFF, _free@GOTPCREL(%rip)
+#endif
+ nop
+
all:
${CC} ${CCFLAGS} main.c foo.c -o main
${FAIL_IF_BAD_MACHO} main
- ${CC} ${CCFLAGS} main.c foo.c -o main -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} main.c foo.c -o main ${VERSION_OLD_LINKEDIT}
${PASS_IFF_GOOD_MACHO} main
clean:
run: all
all:
- ${CC} ${CCFLAGS} -c foo.c -o foo-${ARCH}.o
- ${FAIL_IF_BAD_OBJ} foo-${ARCH}.o
+ ${CC} ${CCFLAGS} -c foo.c -o foo.o
+ ${FAIL_IF_BAD_OBJ} foo.o
- ${CC} ${CCFLAGS} -c foo1.c -o foo1-${ARCH}.o
- ${FAIL_IF_BAD_OBJ} foo1-${ARCH}.o
+ ${CC} ${CCFLAGS} -c foo1.c -o foo1.o
+ ${FAIL_IF_BAD_OBJ} foo1.o
- ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo-${ARCH}.o foo1-${ARCH}.o -o libfoo-${ARCH}.dylib 2>/dev/null
+ ${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -dynamiclib foo.o foo1.o -o libfoo.dylib
clean:
- rm -rf *.o *.dylib main-*
+ rm -rf foo.o foo1.o libfoo.dylib
+
+
+#2>/dev/null
\ No newline at end of file
# can link a program with a large zero-fill section
#
-run: test-run-${ARCH}
+all: test-${FILEARCH}
# i386 catches the problem in the assembler phase
-test-run-i386:
+test-i386:
${PASS_IFF} true
-test-run-ppc test-run-ppc64: test-${ARCH}
-
-test-run-x86_64 test-ppc64:
+test-x86_64:
${CC} ${CCFLAGS} -DSHRINK=1 test.c --save-temps -o test-${ARCH}
${PASS_IFF_GOOD_MACHO} test-${ARCH}
test-ppc:
${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null
-test-run-armv6:
+test-arm:
${PASS_IFF_ERROR} ${CC} ${CCFLAGS} -DSHRINK=4 test.c --save-temps -o test-${ARCH} 2>/dev/null
clean: